<?php

namespace FleekDash\API;

use FleekDash\Core\FilesystemHelper;

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

/**
 * Theme Directory Installer
 *
 * Handles WordPress.org theme installation with progress tracking (sync REST).
 *
 * @package FleekDash\API
 */
class ThemeDirectoryInstaller
{
    private const STEP_INSTALLATION_FAILED = 'Installation failed';

    /**
     * Initialize installer hooks.
     */
    public static function init(): void
    {
        \add_action('fleekdash_install_theme', array(__CLASS__, 'handleLegacyCronEvent'));
        \add_filter(
            'upgrader_source_selection',
            array(ThemeDirectoryPackageResolver::class, 'resolveThemePackageSource'),
            9,
            4,
        );
    }

    /**
     * Drain stale wp-cron events left from pre-sync async installation.
     *
     * @param string $job_id
     */
    public static function handleLegacyCronEvent(string $job_id): void
    {
        $job = \get_transient('fleekdash_install_theme_job_' . $job_id);
        $state = \is_array($job) ? ($job['state'] ?? 'unknown') : 'unknown';

        if ($state === 'complete' || $state === 'failed') {
            fleekdash_log_debug("FleekDash: Legacy theme cron fired for finished job {$job_id} ({$state}); skipping.");
            return;
        }

        fleekdash_log_debug("FleekDash: Legacy theme cron for job {$job_id} in state '{$state}'; marking failed.");
        self::updateJobStatus(
            $job_id,
            'failed',
            0,
            self::STEP_INSTALLATION_FAILED,
            'Stale cron event; retry install via REST.',
        );
    }

    /**
     * Process theme installation (sync REST).
     */
    public static function processInstallation(string $job_id): void
    {
        try {
            if (\function_exists('set_time_limit')) {
                @\set_time_limit(600);
            }
            if (\function_exists('ignore_user_abort')) {
                @\ignore_user_abort(true);
            }
            if (\function_exists('ini_set')) {
                $memory = \defined('WP_MAX_MEMORY_LIMIT') ? WP_MAX_MEMORY_LIMIT : '512M';
                @\ini_set('memory_limit', $memory);
            }

            $job = self::readThemeInstallJobFromTransient($job_id);
            if ($job === null) {
                fleekdash_log_debug('FleekDash: Theme install job not found: ' . $job_id);
                return;
            }

            self::runThemeInstallPipeline($job_id, $job['slug'], $job['activate']);
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Theme installation exception: ' . $e->getMessage());
            self::updateJobStatus($job_id, 'failed', 0, 'Installation error', $e->getMessage());
        }
    }

    /**
     * @return array{slug: string, activate: bool}|null
     */
    private static function readThemeInstallJobFromTransient(string $job_id): ?array
    {
        $job_status = \get_transient('fleekdash_install_theme_job_' . $job_id);
        if ($job_status === false) {
            return null;
        }

        $slug = isset($job_status['slug']) && \is_string($job_status['slug']) ? $job_status['slug'] : '';
        $activate = !empty($job_status['activate']);
        if ($slug === '') {
            return null;
        }

        return array(
            'slug' => $slug,
            'activate' => $activate,
        );
    }

    /**
     * @param string $job_id Job id (transient key suffix)
     * @param string $slug Theme stylesheet slug
     * @param bool $activate Whether to switch to the theme after install
     */
    private static function runThemeInstallPipeline(string $job_id, string $slug, bool $activate): void
    {
        if (self::completePipelineIfThemeAlreadyInstalled($job_id, $slug, $activate)) {
            return;
        }

        self::updateJobStatus($job_id, 'downloading', 10, 'Downloading theme from WordPress.org...');

        require_once \ABSPATH . 'wp-admin/includes/file.php';
        require_once \ABSPATH . 'wp-admin/includes/misc.php';
        require_once \ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        require_once \ABSPATH . 'wp-admin/includes/theme.php';

        $theme_info = self::fetchThemeInformationOrFail($job_id, $slug);
        if ($theme_info === null) {
            return;
        }

        self::executeFreshThemeInstall($job_id, $slug, $activate, $theme_info);
    }

    /**
     * @return bool true when the pipeline finished (success or failed verification)
     */
    private static function completePipelineIfThemeAlreadyInstalled(string $job_id, string $slug, bool $activate): bool
    {
        $installed_themes = \wp_get_themes();
        if (!isset($installed_themes[$slug])) {
            return false;
        }

        fleekdash_log_debug("FleekDash: Theme {$slug} already installed");
        self::updateJobStatus($job_id, 'verifying', 80, 'Theme already installed, verifying...');

        if ($activate) {
            self::updateJobStatus($job_id, 'activating', 90, 'Activating theme...');
            \switch_theme($slug);
            self::updateJobStatus($job_id, 'complete', 100, 'Theme already installed and activated successfully!');
        } else {
            self::updateJobStatus($job_id, 'complete', 100, 'Theme already installed successfully!');
        }

        self::invalidateThemeCaches();
        return true;
    }

    /**
     * @return object|null Theme info object with download_link and name
     */
    private static function fetchThemeInformationOrFail(string $job_id, string $slug): ?object
    {
        $api = \themes_api(
            'theme_information',
            array(
                'slug' => $slug,
                'fields' => array(
                    'sections' => false,
                    'tags' => false,
                ),
            ),
        );

        if (\is_wp_error($api)) {
            self::updateJobStatus(
                $job_id,
                'failed',
                0,
                'Failed to get theme information',
                $api->get_error_message(),
            );
            return null;
        }

        if (!isset($api->download_link) || !\is_string($api->download_link) || $api->download_link === '') {
            self::updateJobStatus(
                $job_id,
                'failed',
                0,
                'Failed to get theme information',
                'Theme not found on WordPress.org',
            );
            return null;
        }

        return $api;
    }

    /**
     * @param object $theme_info
     */
    private static function executeFreshThemeInstall(
        string $job_id,
        string $slug,
        bool $activate,
        object $theme_info
    ): void {
        $name = isset($theme_info->name) && \is_string($theme_info->name) ? $theme_info->name : $slug;
        self::updateJobStatus($job_id, 'downloading', 30, "Downloading {$name}...");

        if (!self::initFilesystemForInstallOrFail($job_id)) {
            return;
        }

        self::removeOrphanThemeDirectoryIfNeeded($slug);
        self::updateJobStatus($job_id, 'installing', 50, 'Installing theme files...');

        if (!self::installThemePackageOrFail($job_id, $slug, (string) $theme_info->download_link)) {
            return;
        }

        if (!self::assertThemeRegisteredAfterInstallOrFail($job_id, $slug)) {
            return;
        }

        self::finalizeThemeInstallWithOptionalActivation($job_id, $slug, $activate);
    }

    private static function initFilesystemForInstallOrFail(string $job_id): bool
    {
        if (!\class_exists(FilesystemHelper::class)) {
            require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/FilesystemHelper.php';
        }

        if (!FilesystemHelper::safe_init()) {
            self::updateJobStatus($job_id, 'failed', 0, 'Filesystem initialization failed');
            return false;
        }

        return true;
    }

    private static function installThemePackageOrFail(string $job_id, string $slug, string $download_link): bool
    {
        $skin = new \WP_Ajax_Upgrader_Skin();
        $upgrader = new \Theme_Upgrader($skin);
        $result = $upgrader->install($download_link);

        if (\is_wp_error($result)) {
            return self::handleInstallFailure($job_id, $slug, $download_link, $upgrader, $result);
        }

        if ($result === false) {
            self::updateJobStatus(
                $job_id,
                'failed',
                0,
                self::STEP_INSTALLATION_FAILED,
                'Unknown error during installation',
            );
            return false;
        }

        return true;
    }

    /**
     * @param \Theme_Upgrader $upgrader
     * @param \WP_Error $result
     */
    private static function handleInstallFailure(
        string $job_id,
        string $slug,
        string $download_link,
        \Theme_Upgrader $upgrader,
        \WP_Error $result
    ): bool {
        $success = false;
        $error_message = $result->get_error_message();
        $folder_exists = \stripos($error_message, 'Destination folder already exists') !== false
            || \stripos($error_message, 'folder already exists') !== false;

        if ($folder_exists && isset(\wp_get_themes()[$slug])) {
            fleekdash_log_debug("FleekDash: Theme {$slug} folder exists but theme is registered; treating as success");
            $success = true;
        } elseif (!$folder_exists) {
            self::updateJobStatus($job_id, 'failed', 0, self::STEP_INSTALLATION_FAILED, $error_message);
        } else {
            fleekdash_log_debug('FleekDash: Removing orphan theme folder and retrying install once');
            self::removeOrphanThemeDirectoryIfNeeded($slug);
            $retry_result = $upgrader->install($download_link);

            if (\is_wp_error($retry_result) || $retry_result === false) {
                $failure_detail = \is_wp_error($retry_result)
                    ? $retry_result->get_error_message()
                    : 'Unknown error during installation';
                self::updateJobStatus($job_id, 'failed', 0, self::STEP_INSTALLATION_FAILED, $failure_detail);
            } else {
                $success = true;
            }
        }

        return $success;
    }

    private static function assertThemeRegisteredAfterInstallOrFail(string $job_id, string $slug): bool
    {
        self::updateJobStatus($job_id, 'verifying', 80, 'Verifying installation...');

        $installed_themes = \wp_get_themes();
        if (!isset($installed_themes[$slug])) {
            self::updateJobStatus(
                $job_id,
                'failed',
                0,
                'Verification failed',
                'Theme not found after installation',
            );
            return false;
        }

        return true;
    }

    /**
     * @param string $job_id Job id (transient key suffix)
     * @param string $slug Theme stylesheet slug
     * @param bool $activate Whether to switch to the theme after install
     */
    private static function finalizeThemeInstallWithOptionalActivation(string $job_id, string $slug, bool $activate): void
    {
        if (!$activate) {
            self::updateJobStatus($job_id, 'complete', 100, 'Theme installed successfully!');
            self::invalidateThemeCaches();
            return;
        }

        self::updateJobStatus($job_id, 'activating', 90, 'Activating theme...');
        \switch_theme($slug);
        self::updateJobStatus($job_id, 'complete', 100, 'Theme installed and activated successfully!');
        self::invalidateThemeCaches();
    }

    /**
     * Delete orphan theme directory when folder exists but WordPress does not register the theme.
     */
    private static function removeOrphanThemeDirectoryIfNeeded(string $slug): void
    {
        $slug_ok = \is_string($slug) && $slug !== ''
            && \preg_match('/^[a-z0-9][a-z0-9-]*$/', $slug) === 1;
        $theme_root = \get_theme_root();
        $dir = \trailingslashit($theme_root) . $slug;

        if (!$slug_ok || !\is_dir($dir) || isset(\wp_get_themes()[$slug])) {
            return;
        }

        $fs = FilesystemHelper::get_instance();
        if ($fs === null || !\method_exists($fs, 'delete')) {
            fleekdash_log_debug("FleekDash: Orphan theme folder cleanup skipped (filesystem unavailable): {$dir}");
            return;
        }

        $deleted = $fs->delete($dir, true);
        if ($deleted) {
            fleekdash_log_debug("FleekDash: Removed orphan/partial theme directory: {$dir}");
            \wp_clean_themes_cache();
            self::invalidateThemeCaches();
            return;
        }

        fleekdash_log_debug("FleekDash: Could not remove orphan theme directory (check permissions): {$dir}");
    }

    private static function invalidateThemeCaches(): void
    {
        \delete_transient('fleekdash_all_updates');
        \delete_site_transient('update_themes');
        \wp_cache_delete('themes', 'themes');
    }

    /**
     * @param string $job_id Job id (transient key suffix)
     * @param string $state Machine-readable status
     * @param int $progress Progress percent (0–100)
     * @param string $step Human-readable step label
     * @param string|null $error Optional error detail
     */
    private static function updateJobStatus(
        string $job_id,
        string $state,
        int $progress,
        string $step,
        ?string $error = null
    ): void {
        $job_status = \get_transient('fleekdash_install_theme_job_' . $job_id);

        if ($job_status === false) {
            $job_status = array(
                'logs' => array(),
            );
        }

        $job_status['state'] = $state;
        $job_status['progress'] = $progress;
        $job_status['step'] = $step;

        $log_entry = '[' . \current_time('mysql') . '] ' . $step;
        if ($error) {
            $log_entry .= ' - Error: ' . $error;
            $job_status['error'] = $error;
        }
        $job_status['logs'][] = $log_entry;

        \set_transient('fleekdash_install_theme_job_' . $job_id, $job_status, 3600);

        if (\defined('WP_DEBUG') && WP_DEBUG) {
            fleekdash_log_debug("FleekDash Theme Install [{$job_id}]: {$state} - {$progress}% - {$step}");
        }
    }
}

ThemeDirectoryInstaller::init();
