<?php

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

use BookiFlex\Core\AbstractPlugin;
use BookiFlex\Core\Admin\AbstractAssetsManager;
use BookiFlex\Core\Admin\Lifecycle\AbstractUninstaller;
use BookiFlex\Core\Admin\Lifecycle\ActivationOrchestrator;
use BookiFlex\Core\Contract\ExtensionActivatorInterface;
use BookiFlex\Core\Contract\Queue\QueueInterface;
use BookiFlex\Core\Infrastructure\Storage\CacheRepositoryInterface;
use BookiFlex\Core\Infrastructure\Storage\OptionRepositoryInterface;
use BookiFlex\Core\ORM\CoreSchema;
use BookiFlex\Core\ORM\SchemaManager;
use BookiFlex\Plugin\Admin\AssetsManager;
use BookiFlex\Plugin\Admin\CPT\AccommodationType;
use BookiFlex\Plugin\Admin\CPT\CancellationPolicy;
use BookiFlex\Plugin\Admin\CPT\PaymentType;
use BookiFlex\Plugin\Admin\CPT\RatePlan;
use BookiFlex\Plugin\Admin\Lifecycle\Uninstaller;
use BookiFlex\Plugin\Container\ServiceProvider;
use Psr\Log\LoggerInterface;
defined( 'ABSPATH' ) || exit;
class Plugin extends AbstractPlugin {
    /**
     * Activation orchestrator instance
     */
    protected ?ActivationOrchestrator $activationOrchestrator = null;

    /**
     * Get Freemius instance (lazy initialization)
     *
     * @return \Freemius
     * @throws \RuntimeException if Freemius is not available
     */
    public function fs() : \Freemius {
        // Check if already in container
        if ( !$this->container->has( 'freemius' ) ) {
            // Safe check for function existence
            if ( !function_exists( 'bookiflex_freemius' ) ) {
                throw new \RuntimeException('Freemius SDK is not available. Make sure bookiflex_freemius() function is defined.');
            }
            // Save reference to already initialized Freemius
            $this->container['freemius'] = bookiflex_freemius();
        }
        $this->container['freemius']->add_filter( 'plugin_icon', fn() => $this->pluginDir . 'assets/img/icon_300px.png' );
        // Show per year price by default on the pricing page
        $this->container['freemius']->add_filter( 'pricing/show_annual_in_monthly', '__return_false' );
        return $this->container['freemius'];
    }

    /**
     * Get plugin edition:
     *  - pro
     *  - free
     */
    public function getEdition() : string {
        return ( $this->fs()->is_premium() && $this->fs()->can_use_premium_code() ? 'pro' : 'free' );
    }

    public function isFreeEdition() : bool {
        return $this->getEdition() === 'free';
    }

    /**
     * Register version-specific services
     */
    protected function registerServices() : void {
        $this->container->register( new ServiceProvider() );
    }

    public function onAfterSetupTheme() : void {
        // Default schema for Free version
        $schemaClass = CoreSchema::class;
        // Add version-specific configurations
        $this->addConfig( [
            'cpt'         => [
                AccommodationType::class,
                CancellationPolicy::class,
                PaymentType::class,
                RatePlan::class
            ],
            'controllers' => [],
            'menus'       => [],
            'schemas'     => [$schemaClass],
            'blocks'      => [\BookiFlex\Plugin\UI\Blocks\SearchFormBlock::class, \BookiFlex\Plugin\UI\Blocks\BookingButtonVariation::class],
        ] );
        // Elementor widgets (no-op unless the Elementor plugin is active)
        ( new \BookiFlex\Plugin\UI\Elementor\ElementorIntegration() )->register();
        parent::onAfterSetupTheme();
    }

    public function init() : void {
        parent::init();
    }

    /**
     * Extensions configuration with explicit load order
     *
     * IMPORTANT: Order matters! Extensions listed first can be used by later ones.
     *
     * Dependencies:
     * - StripePayment/StripePaymentPro requires Payment
     * - OfflinePayment requires Payment
     * - Taxation requires Cart
     * - Fees requires Cart
     * - QuotaManager requires Cart
     *
     * @var array List of extension names to load in order
     */
    protected array $extensionsConfig = [
        // Core extensions (must load first)
        'Taxation',
        'Cart',
        'Reservation',
        'Notification',
        'NotificationLog',
        'NotificationPreview',
        // Fees loaded conditionally in getExtensionsConfig()
        'Payment',
        'Onboarding',
        'BackgroundJobs',
        // Common payment extension
        'OfflinePayment',
        'StripePayment',
    ];

    /**
     * Get extensions configuration
     *
     * @return array List of extension names to load in order
     */
    protected function getExtensionsConfig() : array {
        $extensions = $this->extensionsConfig;
        $extensions[] = 'SimpleQuota';
        return $extensions;
    }

    // =========================================================================
    // LIFECYCLE - NEW ORCHESTRATOR INTEGRATION
    // =========================================================================
    /**
     * Create and configure activation orchestrator
     *
     * This replaces the old createActivator() approach.
     */
    public function createActivationOrchestrator() : ActivationOrchestrator {
        $orchestrator = new ActivationOrchestrator(
            $this,
            $this->container->get( SchemaManager::class ),
            $this->container->get( OptionRepositoryInterface::class ),
            $this->container->get( CacheRepositoryInterface::class ),
            $this->getLogger()
        );
        // Set core schemas
        $orchestrator->setCoreSchemas( $this->getCoreSchemas() );
        // Set extension activators
        $orchestrator->setExtensionActivators( $this->collectExtensionActivators() );
        // Add version update callbacks
        $this->configureUpdateCallbacks( $orchestrator );
        return $orchestrator;
    }

    /**
     * Get core database schemas
     *
     * @return array<class-string>
     */
    protected function getCoreSchemas() : array {
        return [
            \BookiFlex\Core\ORM\Schema\CustomerSchema::class,
            \BookiFlex\Core\ORM\Schema\ReservationAdjustmentSchema::class,
            \BookiFlex\Core\ORM\Schema\ReservationGroupSchema::class,
            \BookiFlex\Core\ORM\Schema\ReservationSchema::class,
            \BookiFlex\Core\ORM\Schema\ReservationAccommodationSchema::class,
            \BookiFlex\Core\ORM\Schema\ReservationExtraSchema::class,
            \BookiFlex\Core\ORM\Schema\DateUnitSchema::class,
            \BookiFlex\Core\ORM\Schema\PenaltySchema::class
        ];
    }

    /**
     * Collect all extension activators
     *
     * Premium-only extensions implement PremiumExtensionInterface
     * and are only instantiated when premium code is available.
     *
     * @return ExtensionActivatorInterface[]
     */
    protected function collectExtensionActivators() : array {
        $queue = $this->container->get( QueueInterface::class );
        $logger = $this->getLogger();
        $activators = [];
        // =====================================================================
        // FREE EXTENSIONS (always included)
        // =====================================================================
        $activators[] = new \BookiFlex\Extensions\Payment\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\Cart\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\Notification\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\Onboarding\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\OfflinePayment\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\Taxation\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\SimpleQuota\Activator($queue, $logger);
        $activators[] = new \BookiFlex\Extensions\Reservation\Activator($queue, $logger);
        return $activators;
    }

    /**
     * Configure version update callbacks
     */
    protected function configureUpdateCallbacks( ActivationOrchestrator $orchestrator ) : void {
        $orchestrator->addUpdateCallback( '1.6.0', function () {
            // Cancel any legacy per-notification Action Scheduler jobs still in the queue
            if ( function_exists( 'as_unschedule_all_actions' ) ) {
                as_unschedule_all_actions( 'bookiflex_cart_cleanup_hourly', [], 'bookiflex' );
            }
        } );
        // v1.4.0: Migrate notifications table from boolean status_sent to enum status column.
        // Poller-based architecture replaces per-notification Action Scheduler tasks.
        $orchestrator->addUpdateCallback( '1.4.0', function () {
            global $wpdb;
            $table = 'bflex_notifications';
            // Only migrate rows that haven't been migrated yet (status is still at default 'PENDING'
            // and status_sent column exists — guard against running twice)
            // phpcs:disable WordPress.DB.DirectDatabaseQuery
            $columnExists = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS\n                     WHERE TABLE_SCHEMA = DATABASE()\n                     AND TABLE_NAME = %s\n                     AND COLUMN_NAME = 'status_sent'", $table ) );
            if ( !$columnExists ) {
                return;
            }
            $wpdb->query( "UPDATE `{$table}` SET `status` = 'SENT'    WHERE `status_sent` = '1' AND `status` = 'PENDING'" );
            // phpcs:ignore
            $wpdb->query( "UPDATE `{$table}` SET `status` = 'PENDING' WHERE `status_sent` = '0' AND `status` = 'PENDING'" );
            // phpcs:ignore
            // $wpdb->query("ALTER TABLE `{$table}` DROP COLUMN `status_sent`"); // phpcs:ignore
            // phpcs:enable
            // Cancel any legacy per-notification Action Scheduler jobs still in the queue
            if ( function_exists( 'as_unschedule_all_actions' ) ) {
                as_unschedule_all_actions( 'bookiflex_send_notification', [], 'bookiflex' );
            }
        } );
    }

    /**
     * Get activation orchestrator instance
     */
    public function getActivationOrchestrator() : ActivationOrchestrator {
        if ( $this->activationOrchestrator === null ) {
            $this->activationOrchestrator = $this->createActivationOrchestrator();
        }
        return $this->activationOrchestrator;
    }

    /**
     * Create version uninstaller
     */
    protected function createUninstaller() : AbstractUninstaller {
        return new Uninstaller();
    }

    /**
     * Create version assets manager
     */
    protected function createAssetsManager() : AbstractAssetsManager {
        $logger = $this->getContainer()->get( LoggerInterface::class );
        return new AssetsManager($this, $logger);
    }

    public static function isDev() : bool {
        return defined( 'BOOKIFLEX_ENV' ) && BOOKIFLEX_ENV === 'development';
    }

}
