<?php

namespace PloverCRM\Core;

/**
 * Main Plugin Class
 * 
 * Bootstrap and orchestrate the entire plugin
 */
class Plugin
{
    /**
     * @var Plugin Singleton instance
     */
    private static $instance = null;

    /**
     * @var Container
     */
    private $container;

    /**
     * @var ModuleManager
     */
    private $moduleManager;

    /**
     * @var Api\ApiManager
     */
    private $apiManager;

    /**
     * @var AdminMenu
     */
    private $adminMenu;

    /**
     * @var array All discovered modules (including disabled)
     */
    private $allDiscoveredModules = [];

    /**
     * @var string Plugin version
     */
    private $version;

    /**
     * @var string Plugin path
     */
    private $pluginPath;

    /**
     * @var string Plugin URL
     */
    private $pluginUrl;

    /**
     * @var \PloverCRM\Core\Services\Logger
     */
    private $logger;

    /**
     * Get logger instance
     */
    private function getLogger(): Services\Logger
    {
        if (!$this->logger) {
            $this->logger = new Services\Logger();
        }
        return $this->logger;
    }

    /**
     * Private constructor (Singleton pattern)
     */
    private function __construct()
    {
        // Initialize container
        $this->container = new Container();

        // Initialize module manager
        $this->moduleManager = new ModuleManager($this->container);

        // Initialize API manager
        $this->apiManager = new Api\ApiManager($this->container);
        $this->apiManager->init();

        // Register core services
        $this->registerCoreServices();
    }

    /**
     * Get singleton instance
     * 
     * @return Plugin
     */
    public static function getInstance(): Plugin
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Initialize the plugin
     * 
     * @param string $pluginFile Main plugin file path
     * @param string $version Plugin version
     * @return void
     */
    public function init(string $pluginFile, string $version): void
    {
        $this->version = $version;
        $this->pluginPath = plugin_dir_path($pluginFile);
        $this->pluginUrl = plugin_dir_url($pluginFile);

        // Define constants
        if (!defined('PLOVER_CRM_PLUGIN_PATH')) {
            define('PLOVER_CRM_PLUGIN_PATH', $this->pluginPath);
        }

        if (!defined('PLOVER_CRM_PLUGIN_URL')) {
            define('PLOVER_CRM_PLUGIN_URL', $this->pluginUrl);
        }

        if (!defined('PLOVER_CRM_PLUGIN_VERSION')) {
            define('PLOVER_CRM_PLUGIN_VERSION', $this->version);
        }

        // Hook into WordPress.
        // Run after other plugins finish their own plugins_loaded bootstrap so
        // CRM providers like FluentCRM have already exposed their helper APIs.
        add_action('plugins_loaded', [$this, 'loadModules'], 100);

        // Disable WP Application Passwords for our API because standalone sign-in
        // is handled by the plugin's own username/password system.
        add_filter('wp_is_application_passwords_available_for_user', [$this, 'disableAppPasswordsForOurApi'], 10, 2);
        add_filter('application_password_is_api_request', [$this, 'blockAppPasswordsForOurApi'], 10, 1);

        // Initialize API Manager
        $this->apiManager->init();

        // Initialize Admin Menu (centralized menu management)
        $this->adminMenu = new AdminMenu($this->container);
        $this->adminMenu->init();

        // Initialize Email Guard Service (block outgoing emails to unsafe addresses)
        $this->container->get('email_guard')->init();
        $this->container->get('reply_to_header')->init();

        // Register centralized log download handler
        \PloverCRM\Common\BaseLogViewer::registerDownloadHandler('logs');
    }

    /**
     * Disable WP Application Passwords availability for our API requests
     * 
     * @param bool $available Whether app passwords are available
     * @param \WP_User $user The user
     * @return bool
     */
    public function disableAppPasswordsForOurApi(bool $available, $user): bool
    {
        if ($this->isOurApiRequest()) {
            return false;
        }
        return $available;
    }

    /**
     * Block WP Application Passwords authentication for our API namespace
     * 
     * @param bool $isApiRequest Whether this is an API request that should use app passwords
     * @return bool
     */
    public function blockAppPasswordsForOurApi(bool $isApiRequest): bool
    {
        if ($this->isOurApiRequest()) {
            return false;
        }
        return $isApiRequest;
    }

    /**
     * Check if current request is for our API namespace
     * 
     * @return bool
     */
    private function isOurApiRequest(): bool
    {
        $requestUri = $_SERVER['REQUEST_URI'] ?? '';
        return strpos($requestUri, '/wp-json/plover-crm/') !== false
            || strpos($requestUri, '?rest_route=/plover-crm/') !== false;
    }

    /**
     * Load and initialize all modules
     * 
     * @return void
     */
    public function loadModules(): void
    {
        // Ensure at least one CRM module is enabled before loading
        $this->ensureCRMModuleEnabled();

        // Rebuild the CRM manager after all plugins have finished booting.
        // Some third-party providers expose their helper APIs during their own
        // plugins_loaded callbacks, and earlier service resolution can cache a
        // stale fallback provider for the rest of the request.
        $this->container->remove('crm');
        $this->container->singleton('crm', function () {
            return new \PloverCRM\Core\CRM\CRMManager();
        });

        // Register CRM hooks
        $crm = $this->container->get('crm');
        $crm->registerHooks();

        // Load module configuration
        $modules = $this->getModuleConfiguration();

        // Get disabled modules
        $disabledModules = get_option('plover_crm_disabled_modules', []);

        // Store all discovered modules and register enabled ones
        foreach ($modules as $moduleClass) {
            try {
                // Create module instance
                $moduleInstance = new $moduleClass($this->container);
                $moduleId = $moduleInstance->getId();

                // Store in all discovered modules (for Module Manager display)
                $this->allDiscoveredModules[$moduleId] = $moduleInstance;

                // Skip registration if module is disabled
                if (in_array($moduleId, $disabledModules)) {
                    continue;
                }

                // Register enabled module
                $this->moduleManager->registerModule($moduleClass);
            } catch (\Exception $e) {
                // Log error but continue loading other modules
                $this->getLogger()->logError('Failed to register module', [
                    'module_class' => $moduleClass,
                    'error' => $e->getMessage()
                ]);
            }
        }

        // Register all modules (calls register() on each)
        $this->moduleManager->registerAll();

        // Boot all modules (calls boot() on each)
        $this->moduleManager->bootAll();

        // Fire action after all modules are loaded
        do_action('plover_crm_modules_loaded', $this);
    }

    /**
     * Get all discovered modules (including disabled ones)
     * 
     * @return array
     */
    public function getAllDiscoveredModules(): array
    {
        return $this->allDiscoveredModules;
    }

    /**
     * Get module configuration by auto-discovering modules
     * 
     * @return array Array of module class names
     */
    private function getModuleConfiguration(): array
    {
        $modules = [];
        $modulesPath = $this->pluginPath . 'app/Modules';

        // Check if modules directory exists
        if (!is_dir($modulesPath)) {
            return apply_filters('plover_crm_modules', $modules);
        }

        // Scan modules directory
        $moduleDirectories = array_diff(scandir($modulesPath), ['.', '..', '.gitkeep']);

        foreach ($moduleDirectories as $moduleDir) {
            $modulePath = $modulesPath . '/' . $moduleDir;

            // Skip if not a directory
            if (!is_dir($modulePath)) {
                continue;
            }

            // Expected module class file: ModuleName/ModuleNameModule.php
            $expectedClassName = $moduleDir . 'Module';
            $expectedFile = $modulePath . '/' . $expectedClassName . '.php';

            // Check if module file exists
            if (file_exists($expectedFile)) {
                $moduleClass = "\\PloverCRM\\Modules\\{$moduleDir}\\{$expectedClassName}";

                // Verify class exists (autoloader should load it)
                if (class_exists($moduleClass)) {
                    $modules[] = $moduleClass;
                } else {
                    $this->getLogger()->logError('Module class not found', [
                        'module_class' => $moduleClass
                    ]);
                }
            }
        }

        // Allow filtering of modules (for adding external modules)
        return apply_filters('plover_crm_modules', $modules);
    }

    /**
     * Ensure at least one CRM module is enabled
     * 
     * @return void
     */
    private function ensureCRMModuleEnabled(): void
    {
        $disabledModules = get_option('plover_crm_disabled_modules', []);
        $crmModules = ['fluent_crm', 'native_crm'];

        // Check if any CRM module is enabled
        $anyCRMEnabled = false;
        foreach ($crmModules as $moduleId) {
            if (!in_array($moduleId, $disabledModules)) {
                $anyCRMEnabled = true;
                break;
            }
        }

        // If no CRM module is enabled, auto-enable Native CRM
        if (!$anyCRMEnabled) {
            $disabledModules = array_diff($disabledModules, ['native_crm']);
            update_option('plover_crm_disabled_modules', array_values($disabledModules));
            $this->getLogger()->logWarning('No CRM module enabled. Auto-enabling Native CRM.');
        }
    }

    /**
     * Register core services in the container
     * 
     * @return void
     */
    private function registerCoreServices(): void
    {
        // Register the container itself
        $this->container->singleton('container', $this->container);

        // Register the module manager
        $this->container->singleton('module_manager', $this->moduleManager);

        // Register the API manager
        $this->container->singleton('api_manager', $this->apiManager);

        // Register the plugin instance
        $this->container->singleton('plugin', $this);

        // Register CRM Manager
        $this->container->singleton('crm', function () {
            return new \PloverCRM\Core\CRM\CRMManager();
        });

        // Register Email Guard Service
        $this->container->singleton('email_guard', function ($c) {
            return new \PloverCRM\Core\Services\EmailGuardService($c);
        });

        $this->container->singleton('reply_to_header', function () {
            return new \PloverCRM\Core\Services\ReplyToHeaderService();
        });
    }

    /**
     * Get the container instance
     * 
     * @return Container
     */
    public function getContainer(): Container
    {
        return $this->container;
    }

    /**
     * Get the module manager instance
     * 
     * @return ModuleManager
     */
    public function getModuleManager(): ModuleManager
    {
        return $this->moduleManager;
    }

    /**
     * Get the API manager instance
     * 
     * @return Api\ApiManager
     */
    public function getApiManager(): Api\ApiManager
    {
        return $this->apiManager;
    }

    /**
     * Get the admin menu instance
     * 
     * @return AdminMenu
     */
    public function getAdminMenu(): AdminMenu
    {
        return $this->adminMenu;
    }

    /**
     * Get plugin version
     * 
     * @return string
     */
    public function getVersion(): string
    {
        return $this->version;
    }

    /**
     * Get plugin path
     * 
     * @return string
     */
    public function getPluginPath(): string
    {
        return $this->pluginPath;
    }

    /**
     * Get plugin URL
     * 
     * @return string
     */
    public function getPluginUrl(): string
    {
        return $this->pluginUrl;
    }

    /**
     * Prevent cloning
     */
    private function __clone() {}

    /**
     * Prevent unserialization
     */
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize singleton");
    }
}
