<?php

namespace GOSP\GravityOps\Core\SuiteCore\Admin;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Renders native GravityOps settings forms inside the admin shell.
 *
 * Provides the shared boilerplate every migrated plugin otherwise re-implements:
 * a `gops-card` wrapped `<form>` posting to `admin-post.php`, labelled fields, and a
 * per-user flash notice for the post-redirect-get success message. Storage and the
 * `admin-post` save handler remain the responsibility of the calling plugin.
 */
class SettingsForm {

	/**
	 * Open a settings form (and optional wrapping card).
	 *
	 * @param string              $action The admin-post action name (hidden `action` input).
	 * @param array<string,mixed> $args   Optional: `nonce_action` (defaults to $action),
	 *                                      `card_title`, `card_intro`, `brand` (bool), `wrap_class`.
	 * @return void
	 */
	public static function open( string $action, array $args = [] ): void {
		$nonce_action = isset( $args['nonce_action'] ) ? (string) $args['nonce_action'] : $action;
		$card_title   = isset( $args['card_title'] ) ? (string) $args['card_title'] : '';
		$card_intro   = isset( $args['card_intro'] ) ? (string) $args['card_intro'] : '';
		$brand        = ! empty( $args['brand'] );
		$wrap_class   = isset( $args['wrap_class'] ) ? (string) $args['wrap_class'] : '';

		if ( '' !== $wrap_class ) {
			echo '<div class="' . esc_attr( $wrap_class ) . '">';
		}

		echo '<div class="gops-card' . ( $brand ? ' gops-card--brand' : '' ) . '">';

		if ( '' !== $card_title ) {
			echo '<h2 class="gops-title">' . esc_html( $card_title ) . '</h2>';
		}
		if ( '' !== $card_intro ) {
			echo '<p>' . esc_html( $card_intro ) . '</p>';
		}

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		echo '<input type="hidden" name="action" value="' . esc_attr( $action ) . '" />';
		wp_nonce_field( $nonce_action );
	}

	/**
	 * Render a labelled settings field.
	 *
	 * Supported types: text, password, email, url, number, textarea, select, checkbox.
	 *
	 * @param string              $type  Field type.
	 * @param string              $name  Field name/id.
	 * @param string              $label Field label.
	 * @param array<string,mixed> $args  Optional: `value`, `description`, `choices`
	 *                                   (value => label), `include_none` (label for a blank
	 *                                   option), `placeholder`, `class` (default regular-text),
	 *                                   `checked` (bool, checkbox), `checkbox_label`, `attributes`
	 *                                   (assoc of extra HTML attributes).
	 * @return void
	 */
	public static function field( string $type, string $name, string $label, array $args = [] ): void {
		$value       = isset( $args['value'] ) ? (string) $args['value'] : '';
		$description = isset( $args['description'] ) ? (string) $args['description'] : '';
		$css_class   = isset( $args['class'] ) ? (string) $args['class'] : 'regular-text';
		$placeholder = isset( $args['placeholder'] ) ? (string) $args['placeholder'] : '';
		$attributes  = isset( $args['attributes'] ) && is_array( $args['attributes'] ) ? $args['attributes'] : [];
		$extra       = self::build_attributes( $attributes );

		$wrap_class = 'gops-field' . ( 'checkbox' === $type ? ' gops-field--checkbox' : '' );
		echo '<div class="' . esc_attr( $wrap_class ) . '">';

		if ( 'checkbox' === $type ) {
			$checkbox_label = isset( $args['checkbox_label'] ) ? (string) $args['checkbox_label'] : $label;
			echo '<label class="gops-field__label" for="' . esc_attr( $name ) . '">' . esc_html( $label ) . '</label>';
			echo '<label>';
			echo '<input type="checkbox" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="1" ' . checked( ! empty( $args['checked'] ), true, false ) . ' ' . $extra . '/> '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $extra is escaped in build_attributes().
			echo esc_html( $checkbox_label );
			echo '</label>';
		} else {
			echo '<label class="gops-field__label" for="' . esc_attr( $name ) . '">' . esc_html( $label ) . '</label>';

			if ( 'select' === $type ) {
				echo '<select id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" class="' . esc_attr( $css_class ) . '" ' . $extra . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $extra is escaped in build_attributes().
				if ( isset( $args['include_none'] ) ) {
					echo '<option value="">' . esc_html( (string) $args['include_none'] ) . '</option>';
				}
				$choices = isset( $args['choices'] ) && is_array( $args['choices'] ) ? $args['choices'] : [];
				foreach ( $choices as $choice_value => $choice_label ) {
					echo '<option value="' . esc_attr( (string) $choice_value ) . '" ' . selected( (string) $choice_value, $value, false ) . '>' . esc_html( (string) $choice_label ) . '</option>';
				}
				echo '</select>';
			} elseif ( 'textarea' === $type ) {
				echo '<textarea id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" class="' . esc_attr( $css_class ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . $extra . '>' . esc_textarea( $value ) . '</textarea>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $extra is escaped in build_attributes().
			} else {
				echo '<input type="' . esc_attr( $type ) . '" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $value ) . '" class="' . esc_attr( $css_class ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . $extra . '/>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $extra is escaped in build_attributes().
			}
		}

		if ( '' !== $description ) {
			echo '<span class="gops-field__desc">' . esc_html( $description ) . '</span>';
		}

		echo '</div>';
	}

	/**
	 * Close a settings form (and its wrapping card).
	 *
	 * @param string              $submit_label Submit button label.
	 * @param array<string,mixed> $args         Optional: `extra_html` (rendered before the submit
	 *                                          button, already escaped), `wrap` (bool, close the
	 *                                          `wrap_class` div opened in open()).
	 * @return void
	 */
	public static function close( string $submit_label = 'Save Settings', array $args = [] ): void {
		if ( ! empty( $args['extra_html'] ) ) {
			echo $args['extra_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Caller-supplied pre-escaped markup.
		}
		echo '<p><button type="submit" class="button button-primary">' . esc_html( $submit_label ) . '</button></p>';
		echo '</form>';
		echo '</div>'; // .gops-card
		if ( ! empty( $args['wrap'] ) ) {
			echo '</div>'; // .wrap_class
		}
	}

	/**
	 * Queue a per-user flash notice to show after the save redirect.
	 *
	 * @param string $slug    Stable per-page slug (namespaces the transient).
	 * @param string $message Message text.
	 * @param string $type    Notice type: success, error, warning, info.
	 * @return void
	 */
	public static function set_notice( string $slug, string $message, string $type = 'success' ): void {
		set_transient(
			self::notice_key( $slug ),
			[
				'message' => $message,
				'type'    => $type,
			],
			60
		);
	}

	/**
	 * Render (and consume) a queued flash notice.
	 *
	 * @param string $slug Stable per-page slug used in set_notice().
	 * @return void
	 */
	public static function render_notice( string $slug ): void {
		$key    = self::notice_key( $slug );
		$notice = get_transient( $key );
		if ( ! is_array( $notice ) || empty( $notice['message'] ) ) {
			return;
		}
		delete_transient( $key );

		$type    = in_array( ( $notice['type'] ?? 'success' ), [ 'success', 'error', 'warning', 'info' ], true ) ? $notice['type'] : 'success';
		$message = (string) $notice['message'];
		echo '<div class="notice notice-' . esc_attr( $type ) . '"><p>' . esc_html( $message ) . '</p></div>';
	}

	/**
	 * Build the per-user transient key for a flash notice.
	 *
	 * @param string $slug Stable per-page slug.
	 * @return string
	 */
	private static function notice_key( string $slug ): string {
		return 'gops_notice_' . sanitize_key( $slug ) . '_' . get_current_user_id();
	}

	/**
	 * Build an escaped attribute string from an associative array.
	 *
	 * @param array<string,scalar> $attributes Attribute name => value pairs.
	 * @return string
	 */
	private static function build_attributes( array $attributes ): string {
		$out = '';
		foreach ( $attributes as $attr => $val ) {
			$out .= esc_attr( $attr ) . '="' . esc_attr( (string) $val ) . '" ';
		}

		return $out;
	}
}
