<?php
/**
 * Schemafy Core Class
 *
 * Main orchestrator that coordinates all plugin components
 *
 * @package Schemafy
 * @since 1.1.85
 * @author SCHEMAFY
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Class SCHEMAFY_Core
 * 
 * Main plugin class that orchestrates all components with dependency injection
 */
class SCHEMAFY_Core {
    
    /**
     * Component instances
     */
    private $validator;
    private $database;
    private $seo_detector;
    private $injector;
    private $rest_api;
    private $admin;
    private $schema_builder;
    private $field_generator;
    private $gutenberg;
    private $auto_generator;
    private $sitemap;
    
    /**
     * Brand information
     * 
     * @var array
     */
    private $brand = array(
        'name' => 'Schemafy',
        'plugin_name' => 'Schemafy',
        'plugin_url' => '',
        'author_url' => '',
        'support_url' => '',
        'docs_url' => '',
        'color' => '#5f70de',
        'secondary_color' => '#3730a3'
    );
    
    /**
     * Constructor
     * Instantiates all classes with dependency injection
     */
    public function __construct() {
        // 1. Instantiate base classes (no dependencies)
        $this->validator = new SCHEMAFY_Validator();
        $this->database = new SCHEMAFY_Database();

        // 2. Instantiate classes with dependencies
        $this->seo_detector = new SCHEMAFY_SEO_Detector($this->validator);
        $this->injector = new SCHEMAFY_Schema_Injector($this->seo_detector, $this->validator);
        $this->rest_api = new SCHEMAFY_REST_API($this->validator, $this->database);
        $this->admin = new SCHEMAFY_Admin($this->validator);
        $this->sitemap = new SCHEMAFY_Sitemap();

        // 3. Instantiate builder classes
        $this->field_generator = new SCHEMAFY_Field_Generator();
        $this->schema_builder = new SCHEMAFY_Schema_Builder($this->field_generator, $this->validator, $this->database);

        // 4. Instantiate Auto Generator (premium-only; class is absent in the
        //    free build, where this stays null and the feature is not offered).
        $this->auto_generator = class_exists('SCHEMAFY_Auto_Generator')
            ? new SCHEMAFY_Auto_Generator($this->database, $this->seo_detector)
            : null;

        // 5. Instantiate Gutenberg integration
        $this->gutenberg = new SCHEMAFY_Gutenberg($this->database);
    }
    
    /**
     * Initialize plugin and register all hooks
     * This is the main entry point after construction
     * 
     * @return void
     */
    public function run() {
        // Load text domain on init (WP 6.7+ warns when textdomains load too
        // early; plugins_loaded fires before the locale is fully determined).
        add_action('init', array($this, 'load_textdomain'));
        
        // Frontend hooks
        add_action('wp_head', array($this->injector, 'inject_schema'), 5);
        add_action('wp_head', array($this->injector, 'inject_meta_tags'), 1);
        add_action('wp_head', array($this->injector, 'inject_social_meta_tags'), 2);

        // Suppress third-party schemas and override SEO plugin meta tags with Schemafy values
        add_action('wp', array($this->injector, 'suppress_third_party_schemas'));
        add_action('wp', array($this->injector, 'register_meta_overrides'), 2);

        // WooCommerce product sync hooks (only when the premium Auto Generator
        // is present — it owns product schema refresh).
        if (class_exists('WooCommerce') && $this->auto_generator) {
            add_action('woocommerce_update_product', array($this->auto_generator, 'maybe_refresh_product_schema'));
            add_action('woocommerce_product_set_stock_status', array($this->auto_generator, 'maybe_refresh_product_schema_by_status'), 10, 3);
        }

        // Admin hooks
        add_action('admin_menu', array($this->admin, 'add_admin_menu'));
        add_action('admin_enqueue_scripts', array($this->admin, 'enqueue_admin_scripts'));
        add_action('admin_notices', array($this->admin, 'show_activation_notice'));
        add_action('admin_bar_menu', array($this->admin, 'add_admin_bar_button'), 100);
        
        // Plugin links
        add_filter('plugin_action_links_' . SCHEMAFY_PLUGIN_BASENAME, array($this->admin, 'add_plugin_links'));
        add_filter('plugin_row_meta', array($this->admin, 'add_plugin_meta'), 10, 2);
        
        // REST API
        add_action('rest_api_init', array($this->rest_api, 'register_routes'));
        
        // Schema Builder
        $this->schema_builder->init();

        // Run database migrations if needed
        add_action('init', array($this, 'maybe_migrate_database'));
    }
    
    /**
     * Activation hook
     * Called when plugin is activated
     * 
     * @return void
     */
    public static function activate() {
        // Rename legacy-prefixed data first. Activating over an older install
        // must not create an empty table alongside the user's existing one and
        // leave their schemas orphaned under the old name.
        schemafy_migrate_legacy_prefix();

        // Create database tables
        $database = new SCHEMAFY_Database();
        $database->create_tables();
        
        // Set activation notice transient
        set_transient('schemafy_schema_activation_notice', true, 30);
        
        // Log activation
        schemafy_log('[SCHEMAFY Schema] Plugin activated successfully');

        if (class_exists('SCHEMAFY_Sitemap')) {
            SCHEMAFY_Sitemap::activate();
        }
    }
    
    /**
     * Deactivation hook
     * Called when plugin is deactivated
     * 
     * @return void
     */
    public static function deactivate() {
        // Clean up transients
        delete_transient('schemafy_schema_activation_notice');
        
        // Log deactivation
        schemafy_log('[SCHEMAFY Schema] Plugin deactivated');

        if (class_exists('SCHEMAFY_Sitemap')) {
            SCHEMAFY_Sitemap::deactivate();
        }
    }
    
    /**
     * Load plugin text domain for translations
     * 
     * @return void
     */
    public function load_textdomain() {
        // Map regional variants to the locales we actually ship, so the plugin
        // translates regardless of the exact country the site picked. Without
        // this, "Português (Portugal)" = pt_PT (no file) fell back to English,
        // and Spanish variants like es_MX / es_CO did not match es_ES.
        add_filter('plugin_locale', array($this, 'normalize_plugin_locale'), 10, 2);

        load_plugin_textdomain(
            'schemafy',
            false,
            dirname(SCHEMAFY_PLUGIN_BASENAME) . '/languages'
        );
    }

    /**
     * Collapse regional locale variants to the bundled translation files.
     *
     * @param string $locale Resolved locale.
     * @param string $domain Text domain being loaded.
     * @return string
     */
    public function normalize_plugin_locale($locale, $domain) {
        if ($domain !== 'schemafy') {
            return $locale;
        }

        if (strpos($locale, 'es_') === 0 || $locale === 'es') {
            return 'es_ES';
        }

        if (strpos($locale, 'pt_') === 0 || $locale === 'pt') {
            return 'pt_BR';
        }

        return $locale;
    }
    
    /**
     * Maybe migrate database
     * Runs database migrations if needed
     * 
     * @return void
     */
    public function maybe_migrate_database() {
        $current_version = get_option('schemafy_schema_db_version', '0');

        if (version_compare($current_version, SCHEMAFY_VERSION, '<')) {
            $this->migrate_database();
            update_option('schemafy_schema_db_version', SCHEMAFY_VERSION);
        }
    }
    
    /**
     * Database migration
     * Adds new columns if they don't exist
     * 
     * @return void
     */
    private function migrate_database() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'schemafy_custom_schemas';
        
        // Check if table exists
        // The table name is a LIKE *value* here, not an identifier, so it can be
        // (and is) bound through prepare().
        $table_exists = $wpdb->get_var( $wpdb->prepare('SHOW TABLES LIKE %s', $table_name) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- schema check on a custom table, not cacheable
        if ($table_exists !== $table_name) {
            // Table doesn't exist, create it
            $this->database->create_tables();
            return;
        }
        
        // Check if columns exist
        $columns = $wpdb->get_col("DESCRIBE $table_name"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
        
        // Add meta_title column if it doesn't exist
        if (!in_array('meta_title', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN meta_title TEXT AFTER schema_data"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added meta_title column to database');
        }
        
        // Add meta_description column if it doesn't exist
        if (!in_array('meta_description', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN meta_description TEXT AFTER meta_title"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added meta_description column to database');
        }
        
        // Add is_active column if it doesn't exist
        if (!in_array('is_active', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN is_active TINYINT(1) DEFAULT 1 AFTER meta_description"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added is_active column to database');
        }
        
        // Add created_at column if it doesn't exist
        if (!in_array('created_at', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN created_at DATETIME AFTER is_active"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added created_at column to database');
        }

        // Add created_by column if it doesn't exist
        if (!in_array('created_by', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN created_by BIGINT(20) UNSIGNED AFTER created_at"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added created_by column to database');
        }

        // Add updated_at column if it doesn't exist
        if (!in_array('updated_at', $columns)) {
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN updated_at DATETIME AFTER created_by"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
            schemafy_log('[SCHEMAFY Schema] Added updated_at column to database');
        }

    }
    
    /**
     * Get brand information
     * 
     * @return array Brand information array
     */
    public function get_brand() {
        return $this->brand;
    }
    
    /**
     * Get validator instance
     * 
     * @return SCHEMAFY_Validator
     */
    public function get_validator() {
        return $this->validator;
    }
    
    /**
     * Get database instance
     * 
     * @return SCHEMAFY_Database
     */
    public function get_database() {
        return $this->database;
    }
    
    /**
     * Get SEO detector instance
     *
     * @return SCHEMAFY_SEO_Detector
     */
    public function get_seo_detector() {
        return $this->seo_detector;
    }

    /**
     * Get injector instance
     *
     * @return SCHEMAFY_Schema_Injector
     */
    public function get_injector() {
        return $this->injector;
    }
    
    /**
     * Get REST API instance
     * 
     * @return SCHEMAFY_REST_API
     */
    public function get_rest_api() {
        return $this->rest_api;
    }
    
    
    /**
     * Get admin instance
     * 
     * @return SCHEMAFY_Admin
     */
    public function get_admin() {
        return $this->admin;
    }
    
    /**
     * Get schema builder instance
     * 
     * @return SCHEMAFY_Schema_Builder
     */
    /**
     * Get schema builder instance
     * 
     * @return SCHEMAFY_Schema_Builder
     */
    public function get_schema_builder() {
        return $this->schema_builder;
    }

    /**
     * Get auto generator instance
     *
     * @return SCHEMAFY_Auto_Generator
     */
    public function get_auto_generator() {
        return $this->auto_generator;
    }

}
