<?php

namespace WPSN\Core;

use WPSN\Admin\AdminMenu;
use WPSN\Admin\AdminBar;
use WPSN\Admin\TasksReturnContextNotice;
use WPSN\Admin\UndoRunsAdmin;
use WPSN\Admin\UserProfileAIPermissions;
use WPSN\Admin\SettingsRepository;
use WPSN\Admin\NetworkSettingsRepository;
use WPSN\SEO\DocumentResolver;
use WPSN\SEO\BreadcrumbsBuilder;
use WPSN\SEO\SchemaGraphBuilder;
use WPSN\SEO\SchemaCustomJsonLdResolver;
use WPSN\SEO\SeoMetaRepository;
use WPSN\SEO\TemplateEngine;
use WPSN\SEO\VariableResolver;

defined('ABSPATH') || exit;

final class Plugin
{
    private Container $container;

    private bool $registered = false;

    public function register(): void
    {
        if ($this->registered) {
            return;
        }
        $this->registered = true;

        $this->container = new Container();

        $this->registerServices($this->container);

        // On version changes we default to the full (Pro) UI so all plugin tools are visible.
        // This is NOT related to licensing.
        $this->maybeUpgradeUiModeOnVersionChange();

        // Perf probe (request-scoped; used by Diagnostics when enabled).
        \WPSN\Core\PerfProbe::init((bool) $this->container->get('settings')->get('performance.admin.collect_metrics', false));

        // Optional persistent perf history (opt-in; admin-only; WP SEO Next pages only).
        \WPSN\Core\PerfHistory::init($this->container->get('settings'));

        // Defensive: keep wp-admin lightweight when Performance Budget is enabled.
        \WPSN\Core\AdminFootprintGuard::init($this->container->get('settings'));

        // Defensive: keep the frontend lightweight (no WPSN assets on public pages).
        \WPSN\Core\FrontendFootprintGuard::init($this->container->get('settings'));

        // Best-effort fatal error tracker (plugin-scoped).
        \WPSN\Core\ErrorTracker::init($this->container->get('settings'));

        // i18n
        add_action('init', static function (): void {
            load_plugin_textdomain('wp-seo-next', false, dirname(plugin_basename(WPSN_PLUGIN_FILE)) . '/languages');
        });

        // Best-effort: encrypt legacy plaintext secret options at rest.
        add_action('init', static function (): void {
            \WPSN\Core\Secrets\SecretsAtRestMigrator::maybeRun();
        }, 1);

        // One-time upgrade hook: install/update caps + flush rewrite rules when plugin version changes.
        // This keeps virtual endpoints (sitemaps, llms.txt, feeds, etc.) working after updates.
        add_action('admin_init', function (): void {
            Capabilities::maybeInstall();

            $stored = get_option('wpsn_version');
            $stored = is_string($stored) ? $stored : '';
            if ($stored !== (string) WPSN_VERSION) {
                $isFreshInstall = ($stored === '' || $stored === '0.0.0');

                // Auto safety net: snapshot settings on updates so users can rollback quickly.
                // (Skip on fresh install.)
                if (!$isFreshInstall) {
                    try {
                        \WPSN\Modules\Stability\ConfigSnapshotService::createAutoSnapshot(
                            sprintf('Auto snapshot: before WP SEO Next update (%s → %s)', $stored !== '' ? $stored : '?', (string) WPSN_VERSION),
                            [
                                'kind' => 'auto_update',
                                'from_version' => $stored,
                                'to_version' => (string) WPSN_VERSION,
                            ]
                        );
                    } catch (\Throwable $e) {
                        // Never block admin on diagnostics.
                    }
                }

                // Head snapshot diff (if a baseline exists).
                try {
                    (new \WPSN\Modules\Stability\HeadDiffService($this->container))->checkDiffOnUpdate($stored, (string) WPSN_VERSION);
                } catch (\Throwable $e) {
                    // Never block admin on diagnostics.
                }

                // On first install, automatically create a baseline head snapshot so future updates can be diffed.
                // This runs only once and is safe even on small/shared hosting.
                if ($isFreshInstall) {
                    try {
                        \WPSN\Modules\Stability\SnapshotsInstaller::maybeInstall();

                        $head = new \WPSN\Modules\Stability\HeadSnapshotService($this->container);
                        if ($head->getBaselineId() <= 0) {
                            $repo = new \WPSN\Modules\Stability\SnapshotRepository();
                            $existing = $repo->list(\WPSN\Modules\Stability\SnapshotRepository::TYPE_HEAD, 1, 0);
                            if (empty($existing)) {
                                $cases = $head->buildDefaultCases();
                                $payload = $head->buildSnapshotPayload($cases);
                                $id = $repo->create(
                                    \WPSN\Modules\Stability\SnapshotRepository::TYPE_HEAD,
                                    'Auto baseline (initial install)',
                                    $payload
                                );
                                if ($id > 0) {
                                    $head->setBaselineId($id);
                                }
                            }
                        }
                    } catch (\Throwable $e) {
                        // Never block admin on diagnostics.
                    }
                }

                update_option('wpsn_version', (string) WPSN_VERSION);
                flush_rewrite_rules(false);
            }
        });

        // Admin settings
        if (is_admin()) {
            (new AdminMenu($this->container))->register();
            (new UndoRunsAdmin($this->container))->register();
            (new TasksReturnContextNotice())->register();
            // Per-user OpenAI overrides (admin-only).
            (new UserProfileAIPermissions())->register();
        }

        // Admin Bar quick toggles (safe mode / output / perf budget).
        // Shown only to privileged users; zero impact for visitors.
        (new AdminBar($this->container))->register();

        // Queue (Stage 11)
        /** @var \WPSN\Core\Queue\QueueKernel $queueKernel */
        $queueKernel = $this->container->get('queue_kernel');
        $queueKernel->register();

        // Optional: external/remote queue runner endpoint (token-protected).
        // Useful on shared hosting where WP-Cron is unreliable or admin-ajax gets 503.
        try {
            /** @var \WPSN\Core\Queue\QueueRemoteRunner $remoteRunner */
            $remoteRunner = $this->container->get('queue_remote_runner');
            $remoteRunner->register();
        } catch (\Throwable $e) {
            // Never block boot.
        }

        // Queue auto-pause (protect shared hosting from retry storms).
        try {
            /** @var \WPSN\Core\Queue\QueueAutoPause $autoPause */
            $autoPause = $this->container->get('queue_autopause');
            $autoPause->register();
        } catch (\Throwable $e) {
            // Never block boot.
        }

// Modules
        /** @var ModuleRegistry $registry */
        $registry = $this->container->get('modules');
        $registry->registerEnabled();

        add_action('init', static function () use ($registry): void {
            $registry->bootEnabled();
        }, 20);

        // WP-CLI commands (optional).
        if (defined('WP_CLI') && (bool) WP_CLI && class_exists('WP_CLI')) {
            try {
                (new \WPSN\Cli\CliKernel($this->container))->register();
            } catch (\Throwable $e) {
                // Never block boot.
            }
        }
    }

    private function registerServices(Container $c): void
    {
        $c->set('settings_repo', static fn () => new SettingsRepository());

        // Multisite network policies (optional; applied by Settings when enabled).
        $c->set('network_settings_repo', static fn () => new NetworkSettingsRepository());
        $c->set('network_settings', static fn (Container $c) => new NetworkSettings($c->get('network_settings_repo')));

        $c->set('settings', static fn (Container $c) => new Settings($c->get('settings_repo'), $c->get('network_settings')));

        $c->set('logger', static fn (Container $c) => new Logger($c->get('settings')->isDebug()));

        $c->set('events', static fn () => new Events());

        $c->set('cache', static fn () => new Cache());

        $c->set('rate_limiter', static fn () => new \WPSN\Core\RateLimiter());

        $c->set('document_resolver', static fn () => new DocumentResolver());

        $c->set('breadcrumbs_builder', function () use ($c) {
            return new BreadcrumbsBuilder($c->get('settings'));
        });

        $c->set('variable_resolver', static fn (Container $c) => new VariableResolver($c->get('settings')));

        $c->set('template_engine', static fn (Container $c) => new TemplateEngine($c->get('settings'), $c->get('variable_resolver')));

        $c->set('schema_custom_json_ld', static fn (Container $c) => new SchemaCustomJsonLdResolver(
            $c->get('settings'),
            $c->get('template_engine'),
            $c->get('logger')
        ));

        $c->set('schema_mapping_helper', static fn () => new \WPSN\SEO\SchemaMappingHelper());
        $c->set('schema_ai_generator', static fn (Container $c) => new \WPSN\SEO\SchemaAiGenerator(
            $c->get('settings'),
            $c->get('logger')
        ));

        $c->set('meta_repo', static fn (Container $c) => new SeoMetaRepository(
            $c->get('settings'),
            $c->get('template_engine'),
            $c->get('cache'),
            $c->get('logger')
        ));

        $c->set('schema_builder', static fn (Container $c) => new SchemaGraphBuilder(
            $c->get('settings'),
            $c->get('logger'),
            $c->get('breadcrumbs_builder'),
            $c->get('schema_custom_json_ld')
        ));
        $c->set('hreflang_resolver', static fn (Container $c) => new \WPSN\SEO\HreflangResolver(
            $c->get('settings')
        ));
        $c->set('public_head_builder', static fn (Container $c) => new \WPSN\SEO\PublicHeadBuilder(
            $c->get('settings'),
            $c->get('meta_repo'),
            $c->get('schema_builder'),
            $c->get('hreflang_resolver')
        ));


                // Queue (Stage 11)
        $c->set('queue', static fn (Container $c) => new \WPSN\Core\Queue\WPDBQueue($c->get('logger')));

        $c->set('queue_runner', static fn (Container $c) => new \WPSN\Core\Queue\QueueRunner(
            $c->get('queue'),
            $c->get('logger')
        ));

        $c->set('queue_remote_runner', static fn (Container $c) => new \WPSN\Core\Queue\QueueRemoteRunner(
            $c->get('queue_runner'),
            $c->get('queue'),
            $c->get('logger'),
            $c->get('rate_limiter')
        ));

        $c->set('queue_kernel', static fn (Container $c) => new \WPSN\Core\Queue\QueueKernel(
            $c->get('queue_runner'),
            $c->get('logger'),
            $c->get('settings')
        ));

        $c->set('queue_autopause', static fn (Container $c) => new \WPSN\Core\Queue\QueueAutoPause(
            $c->get('settings'),
            $c->get('logger')
        ));

$c->set('modules', static fn (Container $c) => new ModuleRegistry($c));
    }

    /**
     * Ensure full UI is enabled after plugin updates.
     *
     * We intentionally run this before the Settings object is first used,
     * so the current request menu can be built with the correct mode.
     */
    private function maybeUpgradeUiModeOnVersionChange(): void
    {
        if (!is_admin()) {
            return;
        }

        // Only touch settings when plugin version changed (or on first install).
        $stored = get_option('wpsn_version');
        $stored = is_string($stored) ? $stored : '';
        if ($stored === (string) WPSN_VERSION) {
            return;
        }

        $settings = get_option(SettingsRepository::OPTION_NAME, []);
        if (!is_array($settings)) {
            $settings = [];
        }

        if (!isset($settings['ui']) || !is_array($settings['ui'])) {
            $settings['ui'] = [];
        }

        // Force full menu visibility after update (user can switch back anytime).
        $settings['ui']['mode'] = UI::MODE_PRO;

        update_option(SettingsRepository::OPTION_NAME, $settings, false);
    }
}
