<?php

require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';

if (!function_exists('fleekdash_log_debug')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

class FleekDashPluginUpgraderSkin extends WP_Upgrader_Skin {
    /** @var array<int, array<string, mixed>> */
    private array $messages = array();
    /** @var array<int, array<string, mixed>> */
    private array $errors = array();
    private string $currentPlugin = '';
    private string $currentPluginName = '';
    private bool $hadErrors = false;

    /**
     * @param array<string, mixed> $args
     */
    public function __construct($args = array()) {
        parent::__construct($args);
        fleekdash_log_debug('FleekDash: Initializing plugin upgrader skin');
    }

    public function header() {
        fleekdash_log_debug('FleekDash: Header called');
        $this->feedback('Starting update process...');
    }

    public function footer() {
        fleekdash_log_debug('FleekDash: Footer called');
        if ($this->hadErrors) {
            $this->feedback('Update process finished with errors');
        } else {
            $this->feedback('Update process finished successfully');
        }

        // Notify the frontend that the run finished
        $this->sendInstallationComplete();
    }

    /**
     * @param string $title
     */
    public function before($title = '') {
        fleekdash_log_debug('FleekDash: Before called with title: ' . $title);

        if (empty($title) && !empty($this->upgrader) && !empty($this->upgrader->skin->plugin)) {
            $title = $this->upgrader->skin->plugin;
        }

        if (!empty($title)) {
            $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $title, false, false);
            $this->currentPlugin = $title;
            $this->currentPluginName = $plugin_data['Name'];
            $this->feedback('Starting update for plugin "%s"...', $this->currentPluginName);
        }
    }

    /**
     * @param string $title
     */
    public function after($title = '') {
        fleekdash_log_debug('FleekDash: After called with title: ' . $title);

        if (!empty($this->currentPluginName)) {
            if ($this->hadErrors) {
                $this->feedback('Failed to update plugin "%s"', $this->currentPluginName);
            } else {
                $this->feedback('Successfully updated plugin "%s"', $this->currentPluginName);
            }
        }
    }

    public function feedback($string, ...$args) {
        fleekdash_log_debug('FleekDash: Feedback called with: ' . print_r(array($string, $args), true));

        if (empty($string)) {
            return;
        }

        $resolvedString = $this->resolveFeedbackString($string);
        if ($resolvedString === '' || strpos($resolvedString, 'maintenance_') === 0) {
            if (strpos($resolvedString, 'maintenance_') === 0) {
                fleekdash_log_debug('FleekDash: Ignoring maintenance message');
            }
            return;
        }

        $message = $this->buildStructuredMessage($resolvedString, $args);
        $this->messages[] = $message;
        $currentStep = isset($message['step']) && is_string($message['step']) ? $message['step'] : null;

        // Push consolidated CustomEvent for SPA listeners
        $this->sendConsolidatedProgress($currentStep, $message);
        fleekdash_log_debug('FleekDash Plugin Upgrade Progress: ' . print_r($message, true));
    }

    /**
     * Map feedback text to a coarse step name
     */
    private function determineCurrentStep(string $message): ?string {
        $step = null;
        if (strpos($message, 'downloading') !== false || strpos($message, 'download') !== false) {
            $step = 'download';
        } elseif (strpos($message, 'unpack') !== false || strpos($message, 'extract') !== false) {
            $step = 'unpack';
        } elseif (strpos($message, 'install') !== false || strpos($message, 'installing') !== false) {
            $step = 'install';
        } elseif (strpos($message, 'updating') !== false || strpos($message, 'update') !== false) {
            $step = 'install'; // Update is similar to install
        } elseif (strpos($message, 'verifying') !== false || strpos($message, 'verify') !== false) {
            $step = 'verify';
        } elseif (strpos($message, 'complete') !== false || strpos($message, 'finished') !== false) {
            $step = 'complete';
        }
        return $step;
    }

    /**
     * Derive 0-100 progress when the message contains a percent literal
     */
    private function calculateStepProgress(string $step, string $message): ?float {
        $progress = null;

        // Prefer explicit percentage substrings
        if (preg_match('/(\d+(?:\.\d+)?)%/', $message, $matches)) {
            $progress = (float) $matches[1];
        } else {
            $progress = $this->getFallbackStepProgress($step, $message);
        }

        return $progress;
    }

    private function getFallbackStepProgress(string $step, string $message): ?float
    {
        $progress = null;
        if ($step === 'download') {
            $progress = $this->matchProgressByKeywords($message, 'starting', 'downloading', 'downloaded');
        } elseif ($step === 'unpack') {
            $progress = $this->matchProgressByKeywords($message, 'unpacking', '', 'unpacked');
        } elseif ($step === 'install') {
            $progress = $this->matchProgressByKeywords($message, 'installing', '', 'installed');
        } elseif ($step === 'verify') {
            $progress = $this->matchProgressByKeywords($message, 'verifying', '', 'verified');
        } elseif ($step === 'complete') {
            $progress = 100.0;
        }
        return $progress;
    }

    private function matchProgressByKeywords(string $message, string $startKeyword, string $middleKeyword, string $finishKeyword): ?float
    {
        $progress = null;
        if ($startKeyword !== '' && strpos($message, $startKeyword) !== false) {
            $progress = 0.0;
        } elseif ($middleKeyword !== '' && strpos($message, $middleKeyword) !== false) {
            $progress = 50.0;
        } elseif ($finishKeyword !== '' && strpos($message, $finishKeyword) !== false) {
            $progress = 100.0;
        }
        return $progress;
    }

    /**
     * Dispatch plugin-install-progress with normalized payload
     */
    /**
     * @param array<string, mixed> $message
     */
    private function sendConsolidatedProgress(?string $step, array $message): void {
        if (!$step) {
            return;
        }

        // Serialize minimal detail for window listeners
        $consolidatedData = array(
            'step' => $step,
            'progress' => isset($message['progress']) ? $message['progress'] : null,
            'message' => $message['message'],
            'plugin' => isset($message['plugin']) ? $message['plugin'] : '',
            'timestamp' => $message['timestamp']
        );

        // Inline script because upgrader runs outside SPA bundle
        $script = sprintf(
            'if (window.dispatchEvent) { window.dispatchEvent(new CustomEvent("plugin-install-progress", { detail: %s })); }',
            json_encode($consolidatedData)
        );

        // Ensure jquery exists for wp_add_inline_script target
        if (!wp_script_is('jquery', 'done')) {
            wp_enqueue_script('jquery');
        }

        wp_add_inline_script('jquery', $script);
    }

    /**
     * Fire plugin-install-complete after footer()
     */
    private function sendInstallationComplete() {
        $script = 'if (window.dispatchEvent) { window.dispatchEvent(new CustomEvent("plugin-install-complete")); }';

        if (!wp_script_is('jquery', 'done')) {
            wp_enqueue_script('jquery');
        }

        wp_add_inline_script('jquery', $script);
    }

    public function error($errors) {
        fleekdash_log_debug('FleekDash: Error called with: ' . print_r($errors, true));
        $this->hadErrors = true;

        if (is_string($errors)) {
            $message = array(
                'message' => $errors,
                'timestamp' => time(),
                'plugin' => $this->currentPluginName,
                'plugin_file' => $this->currentPlugin,
                'type' => 'error'
            );
            $this->errors[] = $message;
            fleekdash_log_debug('FleekDash Plugin Upgrade Error: ' . $errors);
        } elseif (is_wp_error($errors)) {
            foreach ($errors->get_error_messages() as $error) {
                $message = array(
                    'message' => $error,
                    'timestamp' => time(),
                    'plugin' => $this->currentPluginName,
                    'plugin_file' => $this->currentPlugin,
                    'type' => 'error'
                );
                $this->errors[] = $message;
                fleekdash_log_debug('FleekDash Plugin Upgrade Error: ' . $error);
            }
        }
    }

    public function get_upgrade_messages() {
        fleekdash_log_debug('FleekDash: Getting upgrade messages');
        fleekdash_log_debug('FleekDash: Progress messages: ' . print_r($this->messages, true));
        fleekdash_log_debug('FleekDash: Error messages: ' . print_r($this->errors, true));

        $all_messages = array();

        // Merge progress entries
        foreach ($this->messages as $message) {
            $all_messages[] = array(
                'type' => isset($message['type']) ? $message['type'] : $this->getMessageType($message['message']),
                'message' => $message['message'],
                'timestamp' => $message['timestamp'],
                'plugin' => isset($message['plugin']) ? $message['plugin'] : '',
                'plugin_file' => isset($message['plugin_file']) ? $message['plugin_file'] : '',
                'step' => isset($message['step']) ? $message['step'] : ''
            );
        }

        // Append structured errors
        foreach ($this->errors as $error) {
            $all_messages[] = array(
                'type' => 'error',
                'message' => 'Error: ' . $error['message'],
                'timestamp' => $error['timestamp'],
                'plugin' => isset($error['plugin']) ? $error['plugin'] : '',
                'plugin_file' => isset($error['plugin_file']) ? $error['plugin_file'] : ''
            );
        }

        // Chronological ordering for timeline UI
        usort($all_messages, function($a, $b) {
            return $a['timestamp'] - $b['timestamp'];
        });

        // Terminal summary row when any failure occurred
        if ($this->hadErrors) {
            $all_messages[] = array(
                'type' => 'error',
                'message' => 'Update process completed with errors',
                'timestamp' => time()
            );
        }

        return $all_messages;
    }

    private function getMessageType(string $message): string {
        $lowerMessage = strtolower($message);
        $messageType = 'info';
        if (
            strpos($lowerMessage, 'error') !== false ||
            strpos($lowerMessage, 'failed') !== false ||
            strpos($lowerMessage, 'could not') !== false ||
            strpos($lowerMessage, 'unable to') !== false
        ) {
            $messageType = 'error';
        } elseif (
            strpos($lowerMessage, 'success') !== false ||
            strpos($lowerMessage, 'finished') !== false ||
            strpos($lowerMessage, 'completed') !== false
        ) {
            $messageType = 'success';
        } elseif (strpos($lowerMessage, 'downloading') !== false) {
            $messageType = 'downloading';
        } elseif (strpos($lowerMessage, 'unpack') !== false) {
            $messageType = 'unpacking';
        } elseif (strpos($lowerMessage, 'install') !== false) {
            $messageType = 'installing';
        } elseif (strpos($lowerMessage, 'updating') !== false) {
            $messageType = 'updating';
        } elseif (strpos($lowerMessage, 'verifying') !== false) {
            $messageType = 'verifying';
        }
        return $messageType;
    }

    public function decrement_update_count($type) {
        fleekdash_log_debug('FleekDash: Decrement update count called for type: ' . $type);
    }

    /**
     * @param mixed $string
     */
    private function resolveFeedbackString($string): string
    {
        $resolved = '';
        if (is_object($string) && method_exists($string, 'strings') && isset($string->strings[0])) {
            $resolved = (string) $string->strings[0];
        } elseif (is_scalar($string)) {
            $resolved = (string) $string;
        }
        return $resolved;
    }

    /**
     * @param array<int, mixed> $args
     * @return array<string, mixed>
     */
    private function buildStructuredMessage(string $format, array $args): array
    {
        $text = vsprintf($format, $args);
        $message = array(
            'message' => $text,
            'timestamp' => time(),
        );

        if (!empty($this->currentPluginName)) {
            $message['plugin'] = $this->currentPluginName;
            $message['plugin_file'] = $this->currentPlugin;
        }

        $lowerMessage = strtolower($text);
        $currentStep = $this->determineCurrentStep($lowerMessage);
        if ($currentStep) {
            $message['step'] = $currentStep;
            $progress = $this->calculateStepProgress($currentStep, $lowerMessage);
            if ($progress !== null) {
                $message['progress'] = $progress;
            }
        }

        if ($this->isErrorMessage($lowerMessage)) {
            $message['type'] = 'error';
            $this->hadErrors = true;
        }

        return $message;
    }

    private function isErrorMessage(string $lowerMessage): bool
    {
        return strpos($lowerMessage, 'error') !== false
            || strpos($lowerMessage, 'failed') !== false
            || strpos($lowerMessage, 'could not') !== false
            || strpos($lowerMessage, 'unable to') !== false;
    }
}

if (!class_exists('FleekDash_Plugin_Upgrader_Skin', false)) {
    class_alias(FleekDashPluginUpgraderSkin::class, 'FleekDash_Plugin_Upgrader_Skin');
}
