<?php
/**
 * Admin SEO fields for taxonomy term screens.
 *
 * @package SEOvaultSEO
 */

namespace SEOvaultSEO\Admin;

use SEOvaultSEO\Compatibility\Detector;
use SEOvaultSEO\Core\TermMeta;
use SEOvaultSEO\Utils\Guidelines;
use SEOvaultSEO\Utils\Sanitizer;
use SEOvaultSEO\Utils\SeoAssessment;

/**
 * Renders and saves SEO meta on term add/edit screens for public taxonomies.
 */
class TermMetaBox {

	/**
	 * Whether init() has already run.
	 *
	 * @var bool
	 */
	private static $initialized = false;

	/**
	 * Nonce action / field.
	 */
	const NONCE_ACTION = 'seovault_seo_term_meta_box';
	const NONCE_FIELD  = 'seovault_seo_term_meta_box_nonce';

	/**
	 * Initialize term meta box hooks.
	 */
	public static function init() {
		if ( self::$initialized ) {
			return;
		}

		self::$initialized = true;

		add_action( 'admin_init', array( __CLASS__, 'register_taxonomy_hooks' ) );
		add_action( 'created_term', array( __CLASS__, 'save_term' ), 10, 3 );
		add_action( 'edited_term', array( __CLASS__, 'save_term' ), 10, 3 );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
	}

	/**
	 * Register add/edit form hooks for public taxonomies.
	 */
	public static function register_taxonomy_hooks() {
		foreach ( self::get_taxonomies() as $taxonomy ) {
			add_action( "{$taxonomy}_add_form_fields", array( __CLASS__, 'render_add_fields' ) );
			add_action( "{$taxonomy}_edit_form_fields", array( __CLASS__, 'render_edit_fields' ), 10, 2 );
		}
	}

	/**
	 * Public taxonomies that receive SEO fields.
	 *
	 * @return string[]
	 */
	public static function get_taxonomies() {
		$taxonomies = get_taxonomies( array( 'public' => true ), 'names' );
		$excluded   = apply_filters( 'seovaultseo_excluded_taxonomies', array() );

		if ( empty( $excluded ) || ! is_array( $excluded ) ) {
			return array_values( $taxonomies );
		}

		return array_values( array_diff( $taxonomies, $excluded ) );
	}

	/**
	 * Whether the given taxonomy is supported.
	 *
	 * @param string $taxonomy Taxonomy slug.
	 * @return bool
	 */
	public static function is_supported_taxonomy( $taxonomy ) {
		return in_array( $taxonomy, self::get_taxonomies(), true );
	}

	/**
	 * Enqueue assets on term screens.
	 *
	 * @param string $hook Current admin page hook.
	 */
	public static function enqueue_assets( $hook ) {
		if ( ! in_array( $hook, array( 'edit-tags.php', 'term.php' ), true ) ) {
			return;
		}

		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen || empty( $screen->taxonomy ) || ! self::is_supported_taxonomy( $screen->taxonomy ) ) {
			return;
		}

		if ( ! Detector::should_output_seo() ) {
			return;
		}

		wp_enqueue_style(
			'seovaultseo-metabox',
			SEOVAULT_SEO_URL . 'assets/css/seo-metabox.css',
			array(),
			SEOVAULT_SEO_VERSION . '-13'
		);

		wp_enqueue_script(
			'seovaultseo-term-metabox',
			SEOVAULT_SEO_URL . 'assets/js/term-metabox.js',
			array( 'jquery' ),
			SEOVAULT_SEO_VERSION . '-1',
			true
		);

		wp_localize_script(
			'seovaultseo-term-metabox',
			'seovaultseo_term_metabox',
			array(
				'title_limits'    => Guidelines::get_title_limits(),
				'desc_limits'     => Guidelines::get_description_limits(),
				'og_title_limits' => Guidelines::get_og_title_limits(),
				'og_desc_limits'  => Guidelines::get_og_description_limits(),
			)
		);
	}

	/**
	 * Render fields on the "Add term" screen.
	 *
	 * @param string $taxonomy Taxonomy slug.
	 */
	public static function render_add_fields( $taxonomy ) {
		if ( ! self::is_supported_taxonomy( $taxonomy ) ) {
			return;
		}

		if ( ! Detector::should_output_seo() ) {
			self::render_disabled_notice( false );
			return;
		}

		wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD );
		self::render_fields( 0, false );
	}

	/**
	 * Render fields on the "Edit term" screen.
	 *
	 * @param \WP_Term $term     Term object.
	 * @param string   $taxonomy Taxonomy slug.
	 */
	public static function render_edit_fields( $term, $taxonomy ) {
		if ( ! self::is_supported_taxonomy( $taxonomy ) ) {
			return;
		}

		if ( ! Detector::should_output_seo() ) {
			self::render_disabled_notice( true );
			return;
		}

		wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD );
		self::render_fields( (int) $term->term_id, true );
	}

	/**
	 * Save term SEO meta.
	 *
	 * @param int    $term_id  Term ID.
	 * @param int    $tt_id    Term taxonomy ID (unused).
	 * @param string $taxonomy Taxonomy slug.
	 */
	public static function save_term( $term_id, $tt_id = 0, $taxonomy = '' ) {
		unset( $tt_id );

		$term_id  = (int) $term_id;
		$taxonomy = is_string( $taxonomy ) ? $taxonomy : '';

		if ( $term_id <= 0 || '' === $taxonomy || ! self::is_supported_taxonomy( $taxonomy ) ) {
			return;
		}

		if ( ! isset( $_POST[ self::NONCE_FIELD ] ) ) {
			return;
		}

		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE_FIELD ] ) ), self::NONCE_ACTION ) ) {
			return;
		}

		if ( ! self::user_can_edit_term( $term_id, $taxonomy ) ) {
			return;
		}

		if ( ! Detector::should_output_seo() ) {
			return;
		}

		if ( isset( $_POST['seovault_seo_title'] ) ) {
			TermMeta::set_seo_title( $term_id, Sanitizer::sanitize_title( sanitize_text_field( wp_unslash( $_POST['seovault_seo_title'] ) ) ) );
		}

		if ( isset( $_POST['seovault_seo_description'] ) ) {
			TermMeta::set_seo_description( $term_id, Sanitizer::sanitize_description( sanitize_textarea_field( wp_unslash( $_POST['seovault_seo_description'] ) ) ) );
		}

		if ( isset( $_POST['seovault_canonical_url'] ) ) {
			TermMeta::set_canonical_url( $term_id, Sanitizer::sanitize_canonical_url( esc_url_raw( wp_unslash( $_POST['seovault_canonical_url'] ) ) ) );
		}

		$noindex = isset( $_POST['seovault_robots_noindex'] ) ? 1 : 0;
		TermMeta::set_noindex( $term_id, $noindex );

		$nofollow = isset( $_POST['seovault_robots_nofollow'] ) ? 1 : 0;
		TermMeta::set_nofollow( $term_id, $nofollow );

		if ( isset( $_POST['seovault_og_title'] ) ) {
			TermMeta::set_og_title( $term_id, Sanitizer::sanitize_og_title( sanitize_text_field( wp_unslash( $_POST['seovault_og_title'] ) ) ) );
		}

		if ( isset( $_POST['seovault_og_description'] ) ) {
			TermMeta::set_og_description( $term_id, Sanitizer::sanitize_og_description( sanitize_textarea_field( wp_unslash( $_POST['seovault_og_description'] ) ) ) );
		}

		if ( isset( $_POST['seovault_og_image'] ) ) {
			TermMeta::set_og_image( $term_id, Sanitizer::sanitize_og_image( esc_url_raw( wp_unslash( $_POST['seovault_og_image'] ) ) ) );
		}

		SeoAssessment::clear_term_cache( $term_id );
	}

	/**
	 * Capability check for creating/editing a term.
	 *
	 * @param int    $term_id  Term ID.
	 * @param string $taxonomy Taxonomy slug.
	 * @return bool
	 */
	public static function user_can_edit_term( $term_id, $taxonomy ) {
		if ( $term_id > 0 && current_user_can( 'edit_term', $term_id ) ) {
			return true;
		}

		$tax = get_taxonomy( $taxonomy );
		if ( ! $tax ) {
			return false;
		}

		return current_user_can( $tax->cap->edit_terms );
	}

	/**
	 * Render SEO fields for add or edit context.
	 *
	 * @param int  $term_id Term ID (0 on add).
	 * @param bool $is_edit Whether rendering the edit table layout.
	 */
	private static function render_fields( $term_id, $is_edit ) {
		$seo_title       = $term_id ? TermMeta::get_seo_title( $term_id ) : '';
		$seo_description = $term_id ? TermMeta::get_seo_description( $term_id ) : '';
		$canonical_url   = $term_id ? TermMeta::get_canonical_url( $term_id ) : '';
		$noindex         = $term_id ? TermMeta::is_noindex( $term_id ) : false;
		$nofollow        = $term_id ? TermMeta::is_nofollow( $term_id ) : false;
		$og_title        = $term_id ? TermMeta::get_og_title( $term_id ) : '';
		$og_description  = $term_id ? TermMeta::get_og_description( $term_id ) : '';
		$og_image        = $term_id ? TermMeta::get_og_image( $term_id ) : '';

		$title_limits    = Guidelines::get_title_limits();
		$desc_limits     = Guidelines::get_description_limits();
		$og_title_limits = Guidelines::get_og_title_limits();
		$og_desc_limits  = Guidelines::get_og_description_limits();

		$term      = $term_id ? get_term( $term_id ) : null;
		$term_name = ( $term && ! is_wp_error( $term ) ) ? $term->name : '';

		if ( $is_edit ) {
			echo '<tr class="form-field seovault-ai-term-meta-heading"><th colspan="2"><h2>' . esc_html__( 'SEOvault AI', 'seovault-ai' ) . '</h2></th></tr>';
		} else {
			echo '<div class="seovault-ai-term-meta-box seovault-ai-term-meta-box--add">';
			echo '<h2>' . esc_html__( 'SEOvault AI', 'seovault-ai' ) . '</h2>';
		}

		self::field_open( $is_edit, 'seovault_seo_title', __( 'SEO Title', 'seovault-ai' ), $title_limits );
		printf(
			'<input type="text" id="seovault_seo_title" name="seovault_seo_title" value="%s" class="large-text seovaultseo-counted" data-counter-target="title" placeholder="%s" />',
			esc_attr( $seo_title ),
			esc_attr( $term_name ? $term_name : __( 'Enter SEO title', 'seovault-ai' ) )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the term name.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_seo_description', __( 'SEO Description', 'seovault-ai' ), $desc_limits );
		printf(
			'<textarea id="seovault_seo_description" name="seovault_seo_description" rows="3" class="large-text seovaultseo-counted" data-counter-target="description">%s</textarea>',
			esc_textarea( $seo_description )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the term description when available.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_canonical_url', __( 'Canonical URL', 'seovault-ai' ) );
		printf(
			'<input type="url" id="seovault_canonical_url" name="seovault_canonical_url" value="%s" class="large-text" />',
			esc_attr( $canonical_url )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the term archive URL.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_robots_noindex', __( 'Robots', 'seovault-ai' ) );
		echo '<label><input type="checkbox" id="seovault_robots_noindex" name="seovault_robots_noindex" value="1" ' . checked( $noindex, true, false ) . ' /> ';
		echo esc_html__( 'Noindex this term archive', 'seovault-ai' ) . '</label><br />';
		echo '<label><input type="checkbox" id="seovault_robots_nofollow" name="seovault_robots_nofollow" value="1" ' . checked( $nofollow, true, false ) . ' /> ';
		echo esc_html__( 'Nofollow this term archive', 'seovault-ai' ) . '</label>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_og_title', __( 'OG Title', 'seovault-ai' ), $og_title_limits );
		printf(
			'<input type="text" id="seovault_og_title" name="seovault_og_title" value="%s" class="large-text seovaultseo-counted" data-counter-target="og_title" />',
			esc_attr( $og_title )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the SEO title.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_og_description', __( 'OG Description', 'seovault-ai' ), $og_desc_limits );
		printf(
			'<textarea id="seovault_og_description" name="seovault_og_description" rows="3" class="large-text seovaultseo-counted" data-counter-target="og_description">%s</textarea>',
			esc_textarea( $og_description )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the SEO description.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		self::field_open( $is_edit, 'seovault_og_image', __( 'OG Image URL', 'seovault-ai' ) );
		printf(
			'<input type="url" id="seovault_og_image" name="seovault_og_image" value="%s" class="large-text" />',
			esc_attr( $og_image )
		);
		echo '<p class="description">' . esc_html__( 'Leave empty to use the default social image when set.', 'seovault-ai' ) . '</p>';
		self::field_close( $is_edit );

		if ( ! $is_edit ) {
			echo '</div>';
		}
	}

	/**
	 * Open a field wrapper for add or edit layout.
	 *
	 * @param bool        $is_edit Edit table layout.
	 * @param string      $id      Field id.
	 * @param string      $label   Field label.
	 * @param array|null  $limits  Optional char-counter limits.
	 */
	private static function field_open( $is_edit, $id, $label, $limits = null ) {
		$counter = '';
		if ( is_array( $limits ) ) {
			$counter = sprintf(
				' <span class="seovaultseo-char-counter" data-target="%1$s" data-min="%2$s" data-max="%3$s"></span>',
				esc_attr( $id ),
				esc_attr( (string) $limits['ideal_min'] ),
				esc_attr( (string) $limits['ideal_max'] )
			);
		}

		$counter_allowed = array(
			'span' => array(
				'class'       => true,
				'data-target' => true,
				'data-min'    => true,
				'data-max'    => true,
			),
		);

		if ( $is_edit ) {
			echo '<tr class="form-field seovault-ai-term-field">';
			echo '<th scope="row"><label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . wp_kses( $counter, $counter_allowed ) . '</label></th>';
			echo '<td>';
			return;
		}

		echo '<div class="form-field seovault-ai-term-field">';
		echo '<label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . wp_kses( $counter, $counter_allowed ) . '</label>';
	}

	/**
	 * Close a field wrapper.
	 *
	 * @param bool $is_edit Edit table layout.
	 */
	private static function field_close( $is_edit ) {
		if ( $is_edit ) {
			echo '</td></tr>';
			return;
		}
		echo '</div>';
	}

	/**
	 * Disabled-mode notice.
	 *
	 * @param bool $is_edit Edit table layout.
	 */
	private static function render_disabled_notice( $is_edit ) {
		$notice = '<p><strong>' . esc_html__( 'SEOvault AI is disabled.', 'seovault-ai' ) . '</strong> ';
		$notice .= esc_html__( 'SEO output is currently turned off.', 'seovault-ai' ) . '</p>';

		if ( $is_edit ) {
			echo '<tr class="form-field"><th colspan="2">' . $notice . '</th></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
			return;
		}

		echo '<div class="form-field">' . $notice . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
	}
}
