<?php

namespace Licenzo\Services;

use Licenzo\Helper\Settings;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class SettingsPluginDependencyService
{
    /**
     * Get dependency status for a settings key.
     *
     * @param string $settingsKey
     * @return array<string,mixed>
     */
    public function getStatusForSettingsKey(string $settingsKey): array
    {
        $definition = $this->getDefinition($settingsKey);
        $plugins = [];

        foreach ($definition['plugins'] as $pluginDefinition) {
            $plugins[] = $this->getPluginStatus($pluginDefinition);
        }

        return [
            'settings_key' => $settingsKey,
            'label' => (string) ($definition['label'] ?? $settingsKey),
            'description' => (string) ($definition['description'] ?? ''),
            'satisfied' => $this->areRequiredPluginsSatisfied($plugins),
            'plugins' => $plugins,
        ];
    }

    /**
     * Ensure required plugins for a settings key are installed and activated.
     *
     * @param string $settingsKey
     * @return array<string,mixed>
     */
    public function ensureReadyForSettingsKey(string $settingsKey): array
    {
        $definition = $this->getDefinition($settingsKey);
        $steps = [];

        foreach ($definition['plugins'] as $pluginDefinition) {
            $current = $this->getPluginStatus($pluginDefinition);

            if (!empty($current['required']) && empty($current['installed'])) {
                $this->assertCapability('install_plugins');

                $steps[] = [
                    'type' => 'install',
                    'plugin_id' => $current['id'],
                    'status' => 'running',
                    'message' => sprintf('Installing %s…', $current['name']),
                ];

                $this->installPlugin($pluginDefinition);

                $steps[count($steps) - 1] = [
                    'type' => 'install',
                    'plugin_id' => $current['id'],
                    'status' => 'completed',
                    'message' => sprintf('%s installed successfully.', $current['name']),
                ];
            } else {
                $steps[] = [
                    'type' => 'install',
                    'plugin_id' => $current['id'],
                    'status' => 'skipped',
                    'message' => sprintf('%s is already installed.', $current['name']),
                ];
            }

            $current = $this->getPluginStatus($pluginDefinition);

            if (!empty($current['required']) && empty($current['active'])) {
                $this->assertCapability('activate_plugins');

                $steps[] = [
                    'type' => 'activate',
                    'plugin_id' => $current['id'],
                    'status' => 'running',
                    'message' => sprintf('Activating %s…', $current['name']),
                ];

                $this->activatePlugin($pluginDefinition);

                $steps[count($steps) - 1] = [
                    'type' => 'activate',
                    'plugin_id' => $current['id'],
                    'status' => 'completed',
                    'message' => sprintf('%s activated successfully.', $current['name']),
                ];
            } else {
                $steps[] = [
                    'type' => 'activate',
                    'plugin_id' => $current['id'],
                    'status' => 'skipped',
                    'message' => sprintf('%s is already active.', $current['name']),
                ];
            }
        }

        $final = $this->getStatusForSettingsKey($settingsKey);
        $final['steps'] = $steps;

        if (empty($final['satisfied'])) {
            throw new \RuntimeException('Plugin dependency requirements are still not satisfied after install/activate attempt.');
        }

        return $final;
    }

    /**
     * Return supported settings keys (for UI/config validation if needed).
     *
     * @return array<int,string>
     */
    public function getSupportedSettingsKeys(): array
    {
        return array_keys($this->getDefinitions());
    }

    /**
     * @param string $settingsKey
     * @return array<string,mixed>
     */
    private function getDefinition(string $settingsKey): array
    {
        $definitions = $this->getDefinitions();

        if (!isset($definitions[$settingsKey])) {
            throw new \InvalidArgumentException(sprintf('Unsupported settings dependency key: %s', esc_html($settingsKey)));
        }

        return $definitions[$settingsKey];
    }

    /**
     * Central mapping: settings key -> required plugins.
     *
     * Extend here when more setting-gated plugins are introduced.
     *
     * @return array<string,array<string,mixed>>
     */
    private function getDefinitions(): array
    {
        return [
            'commerce_provider_woocommerce' => [
                'label' => 'WooCommerce',
                'description' => 'Requires WooCommerce to process orders and manage products.',
                'plugins' => [
                    [
                        'id' => 'woocommerce',
                        'name' => 'WooCommerce',
                        'slug' => 'woocommerce',
                        'plugin_file' => 'woocommerce/woocommerce.php',
                        'required' => true,
                    ],
                ],
            ],
            'commerce_provider_edd' => [
                'label' => 'Easy Digital Downloads',
                'description' => 'Requires Easy Digital Downloads to process orders and manage products.',
                'plugins' => [
                    [
                        'id' => 'easy-digital-downloads',
                        'name' => 'Easy Digital Downloads',
                        'slug' => 'easy-digital-downloads',
                        'plugin_file' => 'easy-digital-downloads/easy-digital-downloads.php',
                        'required' => true,
                    ],
                ],
            ],
            'enable_subscriptions' => $this->getSubscriptionDependencyDefinition(),
        ];
    }

    /**
     * Resolve subscription dependency based on active commerce provider.
     *
     * WooCommerce requires the separate WooCommerce Stripe Gateway plugin.
     * EDD 3.x bundles Stripe natively — no additional plugin needed.
     *
     * @return array<string,mixed>
     */
    private function getSubscriptionDependencyDefinition(): array
    {
        $provider = Settings::getGeneralSettings()['commerce_provider'] ?? 'woocommerce';

        if ($provider === 'edd') {
            return [
                'label' => 'Enable Subscriptions',
                'description' => 'EDD includes Stripe support natively. No additional plugins required.',
                'plugins' => [],
            ];
        }

        return [
            'label' => 'Enable Subscriptions',
            'description' => 'Requires WooCommerce Stripe Payment Gateway to support Stripe subscription checkout and renewals.',
            'plugins' => [
                [
                    'id' => 'woocommerce-gateway-stripe',
                    'name' => 'WooCommerce Stripe Payment Gateway',
                    'slug' => 'woocommerce-gateway-stripe',
                    'plugin_file' => 'woocommerce-gateway-stripe/woocommerce-gateway-stripe.php',
                    'required' => true,
                ],
            ],
        ];
    }

    /**
     * @param array<string,mixed> $pluginDefinition
     * @return array<string,mixed>
     */
    private function getPluginStatus(array $pluginDefinition): array
    {
        $this->loadPluginAdminApis();

        $pluginFile = (string) ($pluginDefinition['plugin_file'] ?? '');
        $pluginPath = trailingslashit(WP_PLUGIN_DIR) . ltrim($pluginFile, '/');
        $installed = $pluginFile !== '' && file_exists($pluginPath);
        $active = $installed && function_exists('is_plugin_active') && is_plugin_active($pluginFile);

        $version = null;
        if ($installed && function_exists('get_plugins')) {
            $plugins = get_plugins();
            if (isset($plugins[$pluginFile]['Version'])) {
                $version = (string) $plugins[$pluginFile]['Version'];
            }
        }

        return [
            'id' => (string) ($pluginDefinition['id'] ?? $pluginFile),
            'name' => (string) ($pluginDefinition['name'] ?? $pluginFile),
            'slug' => (string) ($pluginDefinition['slug'] ?? ''),
            'plugin_file' => $pluginFile,
            'required' => !empty($pluginDefinition['required']),
            'installed' => $installed,
            'active' => $active,
            'version' => $version,
            'status' => $active ? 'active' : ($installed ? 'inactive' : 'missing'),
        ];
    }

    /**
     * @param array<int,array<string,mixed>> $plugins
     * @return bool
     */
    private function areRequiredPluginsSatisfied(array $plugins): bool
    {
        foreach ($plugins as $plugin) {
            if (!empty($plugin['required']) && (empty($plugin['installed']) || empty($plugin['active']))) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param array<string,mixed> $pluginDefinition
     * @return void
     */
    private function installPlugin(array $pluginDefinition): void
    {
        $slug = (string) ($pluginDefinition['slug'] ?? '');
        if ($slug === '') {
            throw new \RuntimeException('Plugin slug is required for installation.');
        }

        $this->loadPluginInstallerApis();

        $api = plugins_api('plugin_information', [
            'slug' => $slug,
            'fields' => [
                'sections' => false,
                'versions' => false,
                'banners' => false,
            ],
        ]);

        if (is_wp_error($api)) {
            throw new \RuntimeException(esc_html($api->get_error_message()));
        }

        $downloadLink = is_object($api) && isset($api->download_link) ? (string) $api->download_link : '';
        if ($downloadLink === '') {
            throw new \RuntimeException('WordPress.org did not return a valid plugin download URL.');
        }

        $skin = new \Automatic_Upgrader_Skin();
        $upgrader = new \Plugin_Upgrader($skin);
        $result = $upgrader->install($downloadLink);

        if (is_wp_error($result)) {
            throw new \RuntimeException(esc_html($result->get_error_message()));
        }

        if ($result === false) {
            $message = method_exists($skin, 'get_error_messages')
                ? trim((string) $skin->get_error_messages())
                : '';

            if ($message === '' && property_exists($skin, 'result') && is_wp_error($skin->result)) {
                $message = $skin->result->get_error_message();
            }

            throw new \RuntimeException($message !== '' ? esc_html($message) : 'Plugin installation failed.');
        }
    }

    /**
     * @param array<string,mixed> $pluginDefinition
     * @return void
     */
    private function activatePlugin(array $pluginDefinition): void
    {
        $this->loadPluginAdminApis();

        $pluginFile = (string) ($pluginDefinition['plugin_file'] ?? '');
        if ($pluginFile === '') {
            throw new \RuntimeException('Plugin file is required for activation.');
        }

        $result = activate_plugin($pluginFile, '', false, false);
        if (is_wp_error($result)) {
            throw new \RuntimeException(esc_html($result->get_error_message()));
        }
    }

    private function assertCapability(string $capability): void
    {
        if (!function_exists('current_user_can') || !current_user_can($capability)) {
            throw new \RuntimeException(sprintf('Missing required capability: %s', esc_html($capability)));
        }
    }

    private function loadPluginAdminApis(): void
    {
        if (!function_exists('is_plugin_active') || !function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
    }

    private function loadPluginInstallerApis(): void
    {
        $this->loadPluginAdminApis();

        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        if (!class_exists('Plugin_Upgrader')) {
            require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        }

        if (!function_exists('WP_Filesystem')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        if (!class_exists('Automatic_Upgrader_Skin')) {
            require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
        }
    }
}
