<?php

namespace EasyLinkAutomations;

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

use EasyLinkAutomations\Hooks\Hooks;
use EasyLinkAutomations\HTTP\RequestType;
use EasyLinkAutomations\Migration\MigrationHelper;
use EasyLinkAutomations\Utils\Capabilities;
use EasyLinkAutomations\HTTP\Middleware\AdminCheckerMiddleware;
use EasyLinkAutomations\HTTP\Middleware\NonceCheckerMiddleware;
use EasyLinkAutomations\Providers\HookProvider;
use EasyLinkAutomations\Providers\InstallerProvider;
use EasyLinkAutomations\Views\HtmlTagModifier;
use EasyLinkAutomations\Views\Layout;
use EasyLinkAutomations\Views\PluginPageActions;

/**
 * Main plugin orchestrator.
 *
 * Follows the singleton pattern so WordPress only boots the plugin once.
 * All side-effects (hooks, providers, migrations) are registered through
 * dedicated provider classes to keep this file lean and readable.
 */
final class Plugin
{
    private static ?self $instance = null;

    /** @var array<string, object> */
    private array $resolvedMiddleware = [];

    private function __construct()
    {
        $this->registerInstaller();

        new I18n();

        Hooks::addAction('plugins_loaded', [$this, 'loaded']);
    }

    // ──────────────────────────────────────────────
    //  Lifecycle
    // ──────────────────────────────────────────────

    /**
     * Fires on `plugins_loaded` — the earliest safe point to register
     * providers and check DB state.
     */
    public function loaded(): void
    {
        Hooks::doAction(Config::withPrefix('loaded'));

        Hooks::addAction('init', [$this, 'registerProviders'], 8);

        Hooks::addFilter(
            'plugin_action_links_' . Config::get('BASENAME'),
            [new PluginPageActions(), 'renderActionLinks']
        );

        $this->maybeMigrateDB();
    }

    /**
     * Register service providers based on request type.
     */
    public function registerProviders(): void
    {
        if (RequestType::is('admin')) {
            new Layout();
            new HtmlTagModifier();
        }

        // Boot automation provider first (registers triggers & actions)
        // This must happen before HookProvider so routes can access triggers/actions
        (new \EasyLinkAutomations\Providers\AutomationProvider())->boot();

        new HookProvider();
    }

    // ──────────────────────────────────────────────
    //  Middleware
    // ──────────────────────────────────────────────

    /**
     * Middleware map — alias => class.
     *
     * @return array<string, class-string>
     */
    public function middlewares(): array
    {
        return [
            'nonce'   => NonceCheckerMiddleware::class,
            'isAdmin' => AdminCheckerMiddleware::class,
        ];
    }

    public function getMiddleware(string $name): object|false
    {
        if (isset($this->resolvedMiddleware[$name])) {
            return $this->resolvedMiddleware[$name];
        }

        $middlewares = $this->middlewares();

        if (
            isset($middlewares[$name])
            && class_exists($middlewares[$name])
            && method_exists($middlewares[$name], 'handle')
        ) {
            $this->resolvedMiddleware[$name] = new $middlewares[$name]();

            return $this->resolvedMiddleware[$name];
        }

        return false;
    }

    // ──────────────────────────────────────────────
    //  Database migration
    // ──────────────────────────────────────────────

    private function maybeMigrateDB(): void
    {
        if (!Capabilities::check('manage_options')) {
            return;
        }

        if (version_compare(Config::getOption('db_version'), Config::DB_VERSION, '<')) {
            MigrationHelper::migrate(InstallerProvider::migration());
        }
    }

    // ──────────────────────────────────────────────
    //  Installer
    // ──────────────────────────────────────────────

    private function registerInstaller(): void
    {
        (new InstallerProvider())->register();
    }

    // ──────────────────────────────────────────────
    //  Singleton access
    // ──────────────────────────────────────────────

    public static function instance(): ?self
    {
        return self::$instance;
    }

    public static function load(): bool
    {
        if (self::$instance !== null) {
            return false;
        }

        self::$instance = new self();

        return true;
    }
}
