<?php
/**
 * One-time migration from the legacy all-enabled schema catalog.
 *
 * @package SEOvaultSEO\Schema
 */

namespace SEOvaultSEO\Schema;

use SEOvaultSEO\Schema\Validation\SchemaValidationCache;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Converts untouched legacy specialized schemas to opt-in defaults.
 */
class SchemaDefaultsMigration {

	const MARKER_OPTION = 'seovaultseo_schema_defaults_migrated';

	/**
	 * Specialized schema types that must be explicitly enabled.
	 *
	 * @var string[]
	 */
	private static $opt_in_types = array(
		'Product',
		'FAQPage',
		'HowTo',
		'Review',
		'Recipe',
		'Event',
		'Course',
		'SoftwareApplication',
		'JobPosting',
	);

	/**
	 * Whether this migration still needs to run.
	 *
	 * @return bool
	 */
	public static function is_needed() {
		return '1' !== (string) get_option( self::MARKER_OPTION, '' );
	}

	/**
	 * Disable legacy factory-identical specialized schemas.
	 *
	 * Entries with customized fields or display rules are preserved because
	 * they are evidence that the site owner intentionally configured the type.
	 *
	 * @return bool Whether the schema option changed.
	 */
	public static function run() {
		if ( ! self::is_needed() ) {
			return false;
		}

		$saved   = get_option( 'seovaultseo_schemas', array() );
		$changed = false;

		if ( is_array( $saved ) && ! empty( $saved ) ) {
			$factory_by_type = self::factory_by_type();
			foreach ( $saved as $id => $schema ) {
				if ( ! is_array( $schema ) ) {
					continue;
				}

				$type = (string) ( $schema['type'] ?? '' );
				if ( ! in_array( $type, self::$opt_in_types, true ) || empty( $schema['enabled'] ) ) {
					continue;
				}
				if ( empty( $factory_by_type[ $type ] ) || ! self::matches_factory( $schema, $factory_by_type[ $type ] ) ) {
					continue;
				}

				$saved[ $id ]['enabled'] = false;
				$changed                 = true;
			}
		}

		if ( $changed ) {
			update_option( 'seovaultseo_schemas', $saved, false );
			SchemaValidationCache::bump_generation();
		}

		update_option( self::MARKER_OPTION, '1', false );
		return $changed;
	}

	/**
	 * Build factory schemas keyed by type.
	 *
	 * @return array
	 */
	private static function factory_by_type() {
		$result = array();
		foreach ( SchemaUtils::get_default_schemas() as $schema ) {
			if ( is_array( $schema ) && ! empty( $schema['type'] ) ) {
				$result[ (string) $schema['type'] ] = $schema;
			}
		}
		return $result;
	}

	/**
	 * Compare only user-editable fields and display conditions.
	 *
	 * @param array $saved   Saved schema.
	 * @param array $factory Factory schema.
	 * @return bool
	 */
	private static function matches_factory( $saved, $factory ) {
		foreach ( array( 'fields', 'show_on', 'not_show_on' ) as $key ) {
			$saved_value   = $saved[ $key ] ?? array();
			$factory_value = $factory[ $key ] ?? array();
			if ( self::canonicalize( $saved_value ) !== self::canonicalize( $factory_value ) ) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Recursively sort associative keys for stable comparison.
	 *
	 * @param mixed $value Value.
	 * @return mixed
	 */
	private static function canonicalize( $value ) {
		if ( ! is_array( $value ) ) {
			return $value;
		}
		if ( array_keys( $value ) !== range( 0, count( $value ) - 1 ) ) {
			ksort( $value, SORT_STRING );
		}
		foreach ( $value as $key => $item ) {
			$value[ $key ] = self::canonicalize( $item );
		}
		return $value;
	}
}
