<?php

/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */
declare (strict_types = 1);
namespace BookiFlex\Core\Admin\Lifecycle;

use BookiFlex\Core\Contract\ExtensionActivatorInterface;
use BookiFlex\Core\Contract\PluginInterface;
use BookiFlex\Core\Contract\PremiumExtensionInterface;
use BookiFlex\Core\Contract\RoleCapabilitiesInterface;
use BookiFlex\Core\Infrastructure\Storage\CacheRepositoryInterface;
use BookiFlex\Core\Infrastructure\Storage\OptionRepositoryInterface;
use BookiFlex\Core\ORM\SchemaManager;
use BookiFlex\Plugin\Admin\Lifecycle\DefaultSettings\DefaultSettings;
use Psr\Log\LoggerInterface;
defined( 'ABSPATH' ) || exit;
/**
 * Orchestrates plugin activation, deactivation, and license synchronization
 *
 * Handles three types of events:
 * 1. WordPress activation/deactivation hooks
 * 2. Freemius license state changes (via deferred sync)
 * 3. Freemius code upgrades/downgrades
 *
 * Key principle: License changes are handled via a deferred flag because
 * at the moment of Freemius hooks, premium classes may not be loaded yet.
 */
class ActivationOrchestrator {
    // =========================================================================
    // CONSTANTS
    // =========================================================================
    // Cache keys (transients) - without prefix
    private const INSTALLING_LOCK = 'installing';

    // Option keys - without prefix
    private const VERSION_FREE = 'free_version';

    private const VERSION_PRO = 'pro_version';

    private const INSTALLED_FLAG = 'installed';

    private const PENDING_LICENSE_SYNC = 'pending_license_sync';

    private const ONBOARDING_REDIRECT = 'onboarding_needs_redirect';

    private const NEEDS_POSTPONED_SETUP = 'needs_postpone_setup';

    // =========================================================================
    // DEPENDENCIES
    // =========================================================================
    protected PluginInterface $plugin;

    protected SchemaManager $schemaManager;

    protected OptionRepositoryInterface $options;

    protected CacheRepositoryInterface $cache;

    protected ?LoggerInterface $logger;

    /** @var ExtensionActivatorInterface[] */
    protected array $extensionActivators = [];

    /** @var array<class-string> Core schema classes */
    protected array $coreSchemas = [];

    /** @var array<string, callable[]> Version update callbacks */
    protected array $updateCallbacks = [];

    // =========================================================================
    // CONSTRUCTOR
    // =========================================================================
    public function __construct(
        PluginInterface $plugin,
        SchemaManager $schemaManager,
        OptionRepositoryInterface $options,
        CacheRepositoryInterface $cache,
        ?LoggerInterface $logger = null
    ) {
        $this->plugin = $plugin;
        $this->schemaManager = $schemaManager;
        $this->options = $options;
        $this->cache = $cache;
        $this->logger = $logger;
    }

    // =========================================================================
    // REGISTRATION
    // =========================================================================
    /**
     * Register all hooks (WordPress + Freemius)
     */
    public function register() : void {
        $pluginFile = $this->plugin->getContainer()->get( 'plugin_file' );
        // WordPress hooks
        register_activation_hook( $pluginFile, [$this, 'onWordPressActivation'] );
        register_deactivation_hook( $pluginFile, [$this, 'onWordPressDeactivation'] );
        // Version check on init
        $this->plugin->addAction( 'init', [$this, 'checkVersionAndActivate'], 5 );
        // Process pending license sync (must be early, after classes loaded)
        $this->plugin->addAction( 'action_scheduler_init', [$this, 'processPendingLicenseSync'] );
        // Register Freemius hooks
        $this->registerFreemiusHooks();
    }

    /**
     * Register Freemius license and code change hooks
     */
    protected function registerFreemiusHooks() : void {
        $fs = $this->plugin->fs();
        // =====================================================================
        // LICENSE HOOKS - only set flag, process on next request
        // Premium classes may NOT be loaded at this point!
        // =====================================================================
        $fs->add_action( 'after_license_activation', function () {
            $this->logger?->debug( 'Freemius: after_license_activation' );
            $this->markPendingLicenseSync( 'license_activation' );
        } );
        $fs->add_action( 'after_license_deactivation', function () {
            $this->logger?->debug( 'Freemius: after_license_deactivation' );
            $this->markPendingLicenseSync( 'license_deactivation' );
        } );
        $fs->add_action( 'after_license_change', function () {
            $this->logger?->debug( 'Freemius: after_license_change' );
            $this->markPendingLicenseSync( 'license_change' );
        } );
        // =====================================================================
        // CODE CHANGE HOOKS - code is already loaded at this point
        // =====================================================================
        $fs->add_action( 'after_premium_version_activation', function () {
            $this->logger?->debug( 'Freemius: after_premium_version_activation' );
            $this->onCodeUpgrade();
        } );
        $fs->add_action( 'after_free_version_reactivation', function () {
            $this->logger?->debug( 'Freemius: after_free_version_reactivation' );
            // Premium code is still available at this moment!
            $this->onCodeDowngrade();
        } );
    }

    // =========================================================================
    // CONFIGURATION
    // =========================================================================
    /**
     * Set extension activators
     *
     * @param ExtensionActivatorInterface[] $activators
     */
    public function setExtensionActivators( array $activators ) : void {
        $this->extensionActivators = $activators;
    }

    /**
     * Set core database schemas
     *
     * @param array<class-string> $schemas
     */
    public function setCoreSchemas( array $schemas ) : void {
        $this->coreSchemas = $schemas;
    }

    /**
     * Add version update callback
     *
     * @param string $version Target version
     * @param callable $callback Callback to run when updating TO this version
     */
    public function addUpdateCallback( string $version, callable $callback ) : void {
        $this->updateCallbacks[$version][] = $callback;
    }

    // =========================================================================
    // WORDPRESS HOOKS
    // =========================================================================
    /**
     * Handle WordPress plugin activation
     */
    public function onWordPressActivation() : void {
        if ( !$this->canActivate() ) {
            return;
        }
        if ( $this->isInstalling() ) {
            return;
        }
        $this->setInstallingLock();
        try {
            $this->logger?->debug( 'WordPress activation started', [
                'is_premium_code' => $this->isPremiumCode(),
                'can_use_premium' => $this->canUsePremiumFeatures(),
            ] );
            // 1. Database migrations (code-dependent)
            $this->runDatabaseMigrations();
            // 2. First installation (one-time setup)
            if ( $this->isFirstInstallation() ) {
                $this->runFirstInstallation();
            }
            // 3. First activation of this edition
            if ( $this->isFirstEditionActivation() ) {
                $this->runFirstEditionActivation();
            }
            // 4. Version updates
            $this->runVersionUpdates();
            // 5. Sync runtime resources based on current license state
            $this->syncRuntimeResources();
            // 6. Update stored version
            $this->updateStoredVersion();
            // 7. Flush rewrite rules
            flush_rewrite_rules();
            do_action( 'bookiflex_activated', $this->plugin );
        } catch ( \Throwable $e ) {
            $this->logger?->error( 'Activation failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ] );
        } finally {
            $this->clearInstallingLock();
        }
    }

    /**
     * Handle WordPress plugin deactivation
     */
    public function onWordPressDeactivation() : void {
        $this->logger?->debug( 'WordPress deactivation started' );
        // Disable ALL runtime resources (both free and premium)
        foreach ( $this->extensionActivators as $activator ) {
            try {
                $activator->disableRuntimeResources();
            } catch ( \Throwable $e ) {
                $this->logger?->error( 'Extension deactivation failed', [
                    'extension' => get_class( $activator ),
                    'error'     => $e->getMessage(),
                ] );
            }
        }
        // Clear any pending sync flag
        $this->options->delete( self::PENDING_LICENSE_SYNC );
        // Clear plugin hooks
        $this->plugin->clearHooks();
        // If WooCommerce (AS owner) is installed then do nothing otherwise clean feature tick
        if ( !class_exists( 'WooCommerce' ) && function_exists( 'wp_clear_scheduled_hook' ) ) {
            wp_clear_scheduled_hook( 'action_scheduler_run_queue' );
        }
        flush_rewrite_rules();
        do_action( 'bookiflex_deactivated', $this->plugin );
    }

    /**
     * Handle plugin uninstallation
     */
    public function onUninstall() : void {
        $this->logger?->debug( 'Uninstallation started' );
        foreach ( $this->extensionActivators as $activator ) {
            try {
                $activator->uninstall();
            } catch ( \Throwable $e ) {
                $this->logger?->error( 'Extension uninstall failed', [
                    'extension' => get_class( $activator ),
                    'error'     => $e->getMessage(),
                ] );
            }
        }
    }

    // =========================================================================
    // FREEMIUS CODE CHANGE HOOKS
    // =========================================================================
    /**
     * Handle code upgrade (free → premium code)
     */
    public function onCodeUpgrade() : void {
        $this->logger?->debug( 'Code upgrade detected (free → premium)' );
        // Run full activation with new code
        $this->onWordPressActivation();
    }

    /**
     * Handle code downgrade (premium → free code)
     *
     * IMPORTANT: Premium code is STILL available at this moment!
     */
    public function onCodeDowngrade() : void {
        $this->logger?->debug( 'Code downgrade detected (premium → free)' );
        // DB schemas for premium are left intact (preserve data)
    }

    // =========================================================================
    // FREEMIUS LICENSE HOOKS (DEFERRED)
    // =========================================================================
    /**
     * Mark that license sync is needed
     *
     * Called from ANY Freemius license hook.
     * We only set a flag - premium classes may not be loaded yet.
     */
    public function markPendingLicenseSync( string $event ) : void {
        $this->logger?->debug( 'License event detected, marking for sync', [
            'event' => $event,
        ] );
        $this->options->set( self::PENDING_LICENSE_SYNC, [
            'event'     => $event,
            'timestamp' => time(),
        ] );
    }

    /**
     * Process pending license sync if needed
     *
     * Called on 'init' hook. Classes are loaded correctly at this point.
     */
    public function processPendingLicenseSync() : void {
        $pending = $this->options->get( self::PENDING_LICENSE_SYNC );
        if ( $pending === null ) {
            return;
        }
        $this->logger?->debug( 'Processing pending license sync', [
            'event'       => $pending['event'] ?? 'unknown',
            'age_seconds' => time() - ($pending['timestamp'] ?? 0),
        ] );
        // Clear flag first to prevent loops
        $this->options->delete( self::PENDING_LICENSE_SYNC );
        // Sync runtime resources based on current state
        $this->syncRuntimeResources();
    }

    // =========================================================================
    // VERSION CHECK
    // =========================================================================
    /**
     * Check if plugin was updated and run activation if needed
     */
    public function checkVersionAndActivate() : void {
        if ( defined( 'IFRAME_REQUEST' ) ) {
            return;
        }
        $storedVersion = $this->getStoredVersion();
        $currentVersion = $this->plugin->getVersion();
        if ( $storedVersion !== null && version_compare( $storedVersion, $currentVersion, '<' ) ) {
            $this->logger?->debug( 'Version update detected', [
                'from' => $storedVersion,
                'to'   => $currentVersion,
            ] );
            $this->onWordPressActivation();
        }
    }

    // =========================================================================
    // POSTPONED SETUP
    // =========================================================================
    /**
     * Check if postponed setup is needed and run it
     *
     * Call this after Carbon Fields is fully loaded.
     */
    public function runPostponedSetupIfNeeded() : void {
        if ( !$this->options->has( self::NEEDS_POSTPONED_SETUP ) ) {
            return;
        }
        $this->logger?->debug( 'Running postponed setup' );
        DefaultSettings::create();
        // TODO: maybe on hook?
        try {
            do_action( 'bookiflex_postponed_setup', $this->plugin );
            $this->options->delete( self::NEEDS_POSTPONED_SETUP );
        } catch ( \Throwable $e ) {
            $this->logger?->error( 'Postponed setup failed', [
                'error' => $e->getMessage(),
            ] );
        }
    }

    // =========================================================================
    // RUNTIME RESOURCES SYNC
    // =========================================================================
    /**
     * Synchronize runtime resources based on current license state
     */
    public function syncRuntimeResources() : void {
        $this->logger?->debug( 'Syncing runtime resources', [
            'extension_count' => count( $this->extensionActivators ),
        ] );
        foreach ( $this->extensionActivators as $activator ) {
            try {
                $activator->enableRuntimeResources();
            } catch ( \Throwable $e ) {
                $this->logger?->error( 'Failed to sync extension resources', [
                    'extension' => get_class( $activator ),
                    'error'     => $e->getMessage(),
                ] );
            }
        }
    }

    // =========================================================================
    // PRIVATE: INSTALLATION STEPS
    // =========================================================================
    protected function runDatabaseMigrations() : void {
        $schemas = $this->collectAllSchemas();
        $this->logger?->debug( 'Running database migrations', [
            'schema_count' => count( $schemas ),
        ] );
        $this->schemaManager->createOrUpdateTables( $schemas );
    }

    protected function runFirstInstallation() : void {
        $this->logger?->debug( 'Running first installation' );
        // Mark for postponed setup (Carbon Fields)
        $this->options->set( self::NEEDS_POSTPONED_SETUP, true );
        // Mark for onboarding redirect
        $this->options->set( self::ONBOARDING_REDIRECT, true );
        // Create roles and capabilities
        $this->createRolesAndCapabilities();
        // Run extension install hooks
        foreach ( $this->extensionActivators as $activator ) {
            try {
                $activator->install();
            } catch ( \Throwable $e ) {
                $this->logger?->error( 'Extension install failed', [
                    'extension' => get_class( $activator ),
                    'error'     => $e->getMessage(),
                ] );
            }
        }
        // Mark as installed
        $this->options->set( self::INSTALLED_FLAG, true );
        do_action( 'bookiflex_first_installation', $this->plugin );
    }

    protected function runFirstEditionActivation() : void {
        $edition = $this->plugin->getEdition();
        $this->logger?->debug( 'First activation of edition', [
            'edition' => $edition,
        ] );
        do_action( 'bookiflex_first_edition_activation', $this->plugin, $edition );
        do_action( "bookiflex_first_{$edition}_activation", $this->plugin );
    }

    protected function runVersionUpdates() : void {
        $storedVersion = $this->getStoredVersion();
        if ( $storedVersion === null ) {
            return;
            // Fresh install, no updates needed
        }
        // Sort versions
        $versions = array_keys( $this->updateCallbacks );
        usort( $versions, 'version_compare' );
        foreach ( $versions as $version ) {
            if ( version_compare( $storedVersion, $version, '<' ) ) {
                $this->logger?->debug( 'Running update callbacks', [
                    'target_version' => $version,
                ] );
                foreach ( $this->updateCallbacks[$version] as $callback ) {
                    try {
                        $callback( $this->plugin );
                    } catch ( \Throwable $e ) {
                        $this->logger?->error( 'Update callback failed', [
                            'version' => $version,
                            'error'   => $e->getMessage(),
                        ] );
                    }
                }
            }
        }
    }

    // =========================================================================
    // PRIVATE: HELPERS
    // =========================================================================
    protected function canActivate() : bool {
        return is_blog_installed() && current_user_can( 'activate_plugins' );
    }

    protected function isInstalling() : bool {
        return $this->cache->get( self::INSTALLING_LOCK ) === 'yes';
    }

    protected function setInstallingLock() : void {
        $this->cache->set( self::INSTALLING_LOCK, 'yes', MINUTE_IN_SECONDS * 10 );
    }

    protected function clearInstallingLock() : void {
        $this->cache->delete( self::INSTALLING_LOCK );
    }

    protected function isFirstInstallation() : bool {
        return !$this->options->has( self::INSTALLED_FLAG );
    }

    protected function isFirstEditionActivation() : bool {
        $versionKey = $this->getVersionOptionKey();
        return !$this->options->has( $versionKey );
    }

    protected function getStoredVersion() : ?string {
        return $this->options->get( $this->getVersionOptionKey() );
    }

    protected function updateStoredVersion() : void {
        $this->options->set( $this->getVersionOptionKey(), $this->plugin->getVersion() );
    }

    protected function getVersionOptionKey() : string {
        return ( $this->isPremiumCode() ? self::VERSION_PRO : self::VERSION_FREE );
    }

    /**
     * Check if premium CODE is available (not license!)
     */
    protected function isPremiumCode() : bool {
        return $this->plugin->fs()->is_premium();
    }

    /**
     * Check if premium FEATURES can be used (code + license)
     */
    protected function canUsePremiumFeatures() : bool {
        return !$this->plugin->isFreeEdition();
    }

    /**
     * Collect schemas from all sources
     *
     * @return array<class-string>
     */
    protected function collectAllSchemas() : array {
        $schemas = $this->coreSchemas;
        foreach ( $this->extensionActivators as $activator ) {
            $schemas = array_merge( $schemas, $activator->getSchemas() );
        }
        return $schemas;
    }

    protected function createRolesAndCapabilities() : void {
        global $wp_roles;
        if ( !isset( $wp_roles ) ) {
            $wp_roles = new \WP_Roles();
        }
        // Add hotel manager role
        add_role( 'bookiflex_hotel_manager', __( 'Hotel Manager', 'bookiflex' ), [
            'read'         => true,
            'upload_files' => true,
            'edit_files'   => true,
        ] );
        // Get all capabilities
        $capabilities = $this->getCapabilities();
        // Assign to roles
        foreach ( $capabilities as $cap ) {
            $wp_roles->add_cap( 'bookiflex_hotel_manager', $cap );
            $wp_roles->add_cap( 'administrator', $cap );
        }
    }

    /**
     * Get all capabilities
     */
    protected function getCapabilities() : array {
        return [
            RoleCapabilitiesInterface::MANAGE_CONFIGURATION,
            RoleCapabilitiesInterface::MANAGE_PAYMENTS,
            RoleCapabilitiesInterface::MANAGE_RESERVATIONS,
            RoleCapabilitiesInterface::MANAGE_CUSTOMERS,
            RoleCapabilitiesInterface::DELETE_CONFIGURATION
        ];
    }

}
