<?php

namespace FleekDash\API;

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

use WP_REST_Request;
use WP_Error;
use FleekDash\Core\AuthMiddleware;
use FleekDash\Core\FleekDashRollbackRegistry;
use FleekDash\Core\PluginHelper;

/**
 * Rollback Management API
 *
 * Provides rollback/downgrade functionality for plugins and themes
 * WordPress doesn't have native rollback, so we create it!
 *
 * Features:
 * - Install specific version of plugin/theme
 * - List available versions from WordPress.org
 * - Automatic backup before rollback
 * - Async rollback with job tracking
 *
 * Routes are registered via {@see \FleekDash\Controllers\RollbackController} and PluginsRoutes.
 *
 * @package FleekDash\API
 */
class RollbackManagementAPI
{
    /**
     * Cache duration for version lists
     */
    const CACHE_DURATION = 86400; // 24 hours

    private const MESSAGE_ADMIN_ACCESS_REQUIRED = 'Admin access required';
    private const MESSAGE_FLEEKDASH_ONLY = 'Rollback currently supports only the FleekDash plugin';
    private const MESSAGE_ROLLBACK_VALIDATION_FAILED = 'Rollback validation failed';

    private const REL_WP_ADMIN_PLUGIN_INSTALL = 'wp-admin/includes/plugin-install.php';

    /**
     * Get available versions for plugin or theme
     */
    public function getVersions(WP_REST_Request $request)
    {
        $response = null;
        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                fleekdash_log_debug('FleekDash: Versions check without auth: ' . $auth_check['reason']);
            }

            $raw_type = $request->get_param('type');
            $raw_slug = $request->get_param('slug');
            $type = \is_string($raw_type) ? $raw_type : '';
            $slug = \is_string($raw_slug) ? $raw_slug : '';

            $cache_key = 'fleekdash_versions_' . $type . '_' . $slug;
            $cached = \get_transient($cache_key);

            if ($cached !== false) {
                $response = \rest_ensure_response($cached);
            } else {
                $versions = $type === 'plugin'
                    ? $this->getPluginVersions($slug, $this->getCurrentPluginVersion($slug))
                    : $this->getThemeVersions($slug);

                if (empty($versions)) {
                    $response = new WP_Error('no_versions', 'No versions found for ' . $slug, array('status' => 404));
                } else {
                    $current_version = $this->getCurrentVersion($type, $slug);

                    $response_data = array(
                        'success' => true,
                        'type' => $type,
                        'slug' => $slug,
                        'current_version' => $current_version,
                        'versions' => $versions,
                        'total_versions' => \count($versions),
                    );

                    \set_transient($cache_key, $response_data, self::CACHE_DURATION);

                    $response = \rest_ensure_response($response_data);
                }
            }
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Get versions error: ' . $e->getMessage());
            $response = new WP_Error('versions_error', 'Error getting versions', array('status' => 500));
        }

        return $response;
    }

    /**
     * Start rollback process
     */
    public function startRollback(WP_REST_Request $request)
    {
        $response = null;
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                $response = new WP_Error('unauthorized', self::MESSAGE_ADMIN_ACCESS_REQUIRED, array('status' => 403));
            } else {
                $type = $request->get_param('type');
                $item = $request->get_param('item');
                $version = $request->get_param('version');
                $backup = (bool) $request->get_param('backup');
                $dry_run = (bool) $request->get_param('dry_run');

                if ($type !== 'plugin' || !FleekDashRollbackRegistry::isFleekDashPluginFile((string) $item)) {
                    $response = new WP_Error('unsupported_rollback_target', self::MESSAGE_FLEEKDASH_ONLY, array('status' => 400));
                } else {
                    $validation = $this->buildFleekDashRollbackValidation((string) $item, \is_string($version) ? $version : '', $backup);
                    if ($dry_run) {
                        $response = $this->buildDryRunResponse($validation);
                    } elseif (!$validation['eligible']) {
                        $response = new WP_Error(
                            'rollback_validation_failed',
                            self::MESSAGE_ROLLBACK_VALIDATION_FAILED,
                            array(
                                'status' => 400,
                                'validation' => $validation,
                            )
                        );
                    } else {
                        $job_id = 'rollback_' . $type . '_' . \time();
                        $job_status = $this->buildRollbackJobStatus(
                            $type,
                            (string) $item,
                            (string) $version,
                            $backup,
                            (string) $validation['download_url']
                        );

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

                        \wp_schedule_single_event(\time(), 'fleekdash_process_rollback', array($job_id));

                        $response = \rest_ensure_response(array(
                            'success' => true,
                            'job_id' => $job_id,
                            'message' => 'Rollback started',
                            'status' => $job_status,
                        ));
                    }
                }
            }
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Start rollback error: ' . $e->getMessage());
            $response = new WP_Error('start_rollback_error', 'Error starting rollback', array('status' => 500));
        }

        return $response;
    }

    /**
     * Get rollback job status
     */
    public function getRollbackStatus(WP_REST_Request $request)
    {
        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                fleekdash_log_debug('FleekDash: Rollback status check without auth');
            }

            $job_id = $request->get_param('job_id');

            $job_status = \get_transient('fleekdash_rollback_job_' . $job_id);

            if ($job_status === false) {
                return new WP_Error('job_not_found', 'Rollback job not found', array('status' => 404));
            }

            return \rest_ensure_response(array(
                'success' => true,
                'job_id' => $job_id,
                'status' => $job_status,
            ));
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Get rollback status error: ' . $e->getMessage());
            return new WP_Error('status_error', 'Error getting status', array('status' => 500));
        }
    }

    /**
     * Cancel rollback
     */
    public function cancelRollback(WP_REST_Request $request)
    {
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                return new WP_Error('unauthorized', self::MESSAGE_ADMIN_ACCESS_REQUIRED, array('status' => 403));
            }

            $job_id = $request->get_param('job_id');

            \delete_transient('fleekdash_rollback_job_' . $job_id);

            return \rest_ensure_response(array(
                'success' => true,
                'message' => 'Rollback cancelled',
            ));
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Cancel rollback error: ' . $e->getMessage());
            return new WP_Error('cancel_error', 'Error cancelling rollback', array('status' => 500));
        }
    }

    /**
     * List available backups
     */
    public function listBackups(WP_REST_Request $request)
    {
        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                fleekdash_log_debug('FleekDash: List backups without auth');
            }

            $raw_type = $request->get_param('type');
            $raw_slug = $request->get_param('slug');
            $type = \is_string($raw_type) ? $raw_type : '';
            $slug = \is_string($raw_slug) ? $raw_slug : '';

            $backup_dir = WP_CONTENT_DIR . '/fleekdash-backups/' . $type . 's/' . $slug;

            if (!\is_dir($backup_dir)) {
                return \rest_ensure_response(array(
                    'success' => true,
                    'backups' => array(),
                ));
            }

            $backups = array();
            $files = \scandir($backup_dir);

            foreach ($files as $file) {
                if ($file === '.' || $file === '..') {
                    continue;
                }

                $file_path = $backup_dir . '/' . $file;
                if (!\is_file($file_path)) {
                    continue;
                }

                \preg_match('/-([0-9.]+)-backup-(\d+)\.zip$/', $file, $matches);

                if (!empty($matches)) {
                    $backups[] = array(
                        'filename' => $file,
                        'version' => $matches[1],
                        'timestamp' => $matches[2],
                        'date' => \date('Y-m-d H:i:s', (int) $matches[2]),
                        'size' => \filesize($file_path),
                        'size_formatted' => \size_format(\filesize($file_path)),
                    );
                }
            }

            \usort($backups, function ($a, $b) {
                return $b['timestamp'] - $a['timestamp'];
            });

            return \rest_ensure_response(array(
                'success' => true,
                'backups' => $backups,
            ));
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: List backups error: ' . $e->getMessage());
            return new WP_Error('backups_error', 'Error listing backups', array('status' => 500));
        }
    }

    /**
     * Restore from backup
     */
    public function restoreBackup(WP_REST_Request $request)
    {
        $out = null;
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                $out = new WP_Error('unauthorized', self::MESSAGE_ADMIN_ACCESS_REQUIRED, array('status' => 403));
            } else {
                $backup_file = $request->get_param('backup_file');

                $backup_file = \basename($backup_file);
                $backup_path = FleekDashRollbackRegistry::getBackupPath((string) $backup_file);

                if ($backup_path === null || !\file_exists($backup_path)) {
                    $out = new WP_Error('backup_not_found', 'Backup file not found', array('status' => 404));
                } else {
                    $job_id = 'restore_backup_' . \time();

                    $job_status = array(
                        'state' => 'pending',
                        'progress' => 0,
                        'step' => 'Preparing to restore backup...',
                        'started_at' => \current_time('mysql'),
                        'logs' => array(
                            '[' . \current_time('mysql') . '] Restore job created',
                        ),
                        'backup_file' => $backup_file,
                    );

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

                    \wp_schedule_single_event(\time(), 'fleekdash_process_restore', array($job_id, $backup_path));

                    $out = \rest_ensure_response(array(
                        'success' => true,
                        'job_id' => $job_id,
                        'message' => 'Restore started',
                        'status' => $job_status,
                    ));
                }
            }
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Restore backup error: ' . $e->getMessage());
            $out = new WP_Error('restore_error', 'Error restoring backup', array('status' => 500));
        }

        return $out;
    }

    /**
     * Get plugin versions from WordPress.org
     */
    private function getPluginVersions(string $slug, ?string $current_version = null)
    {
        $versions = array();
        if (FleekDashRollbackRegistry::isFleekDashPluginSlug($slug)) {
            $versions = FleekDashRollbackRegistry::getRollbackVersions($current_version, 5);
        } else {
            $versions = $this->getWordPressOrgPluginVersions($slug);
        }
        return $versions;
    }

    /**
     * @return array<string, mixed>|null
     */
    private function buildRollbackJobStatus(
        string $type,
        string $item,
        string $version,
        bool $backup,
        string $download_url
    ): array
    {
        return array(
            'state' => 'pending',
            'progress' => 0,
            'step' => 'Preparing rollback...',
            'started_at' => \current_time('mysql'),
            'logs' => array(
                '[' . \current_time('mysql') . '] Rollback job created',
                '[' . \current_time('mysql') . '] Type: ' . $type . ', Item: ' . $item . ', Version: ' . $version,
            ),
            'type' => $type,
            'item' => $item,
            'version' => $version,
            'backup' => $backup,
            'download_url' => $download_url,
        );
    }

    /**
     * @return array{
     *   eligible: bool,
     *   type: string,
     *   item: string,
     *   target_version: string,
     *   current_version: string|null,
     *   backup_enabled: bool,
     *   download_url: string,
     *   checks: array<string, bool>,
     *   warnings: list<string>
     * }
     */
    private function buildFleekDashRollbackValidation(string $item, string $version, bool $backup): array
    {
        $current_version = $this->getCurrentPluginVersion(FleekDashRollbackRegistry::PLUGIN_SLUG);
        $download_url = $this->resolveFleekDashRollbackDownloadUrl($version, $current_version);
        $plugin_directory = WP_PLUGIN_DIR . '/' . FleekDashRollbackRegistry::PLUGIN_SLUG;
        $backup_directory = WP_CONTENT_DIR . '/fleekdash-backups/plugins/' . FleekDashRollbackRegistry::PLUGIN_SLUG;
        $checks = [
            'plugin_exists' => \is_dir($plugin_directory) && \file_exists($plugin_directory . '/fleekdash.php'),
            'download_url_resolved' => $download_url !== '',
            'target_version_available' => $download_url !== '',
            'target_is_older' => $current_version !== null && \version_compare($version, $current_version, '<'),
            'plugin_directory_writable' => \is_dir($plugin_directory) && \is_writable($plugin_directory),
            'zip_archive_available' => \class_exists('ZipArchive'),
            'backup_directory_ready' => $this->isBackupDirectoryReady($backup_directory),
        ];
        $warnings = $this->buildRollbackValidationWarnings($checks, $version, $current_version, $backup);
        $eligible = $checks['plugin_exists']
            && $checks['download_url_resolved']
            && $checks['target_version_available']
            && $checks['target_is_older']
            && $checks['plugin_directory_writable']
            && (!$backup || ($checks['zip_archive_available'] && $checks['backup_directory_ready']));

        return [
            'eligible' => $eligible,
            'type' => 'plugin',
            'item' => $item,
            'target_version' => $version,
            'current_version' => $current_version,
            'backup_enabled' => $backup,
            'download_url' => $download_url,
            'checks' => $checks,
            'warnings' => $warnings,
        ];
    }

    /**
     * @param array{
     *   eligible: bool,
     *   type: string,
     *   item: string,
     *   target_version: string,
     *   current_version: string|null,
     *   backup_enabled: bool,
     *   download_url: string,
     *   checks: array<string, bool>,
     *   warnings: list<string>
     * } $validation
     */
    private function buildDryRunResponse(array $validation)
    {
        return \rest_ensure_response([
            'success' => true,
            'dry_run' => true,
            'message' => $validation['eligible']
                ? 'Rollback validation passed'
                : 'Rollback validation found issues',
            'validation' => $validation,
        ]);
    }

    private function resolveFleekDashRollbackDownloadUrl(string $version, ?string $current_version = null): string
    {
        $download_url = '';
        $available_versions = FleekDashRollbackRegistry::getRollbackVersions($current_version, 20);
        foreach ($available_versions as $available_version) {
            if (($available_version['version'] ?? '') === $version) {
                $download_url = (string) ($available_version['download_url'] ?? '');
                break;
            }
        }

        return $download_url;
    }

    private function isBackupDirectoryReady(string $backup_directory): bool
    {
        if (\is_dir($backup_directory)) {
            return \is_writable($backup_directory);
        }

        $parent_directory = \dirname($backup_directory);

        return \is_dir($parent_directory) && \is_writable($parent_directory);
    }

    /**
     * @param array<string, bool> $checks
     * @return list<string>
     */
    private function buildRollbackValidationWarnings(
        array $checks,
        string $version,
        ?string $current_version,
        bool $backup
    ): array {
        $warnings = [];
        if (!$checks['plugin_exists']) {
            $warnings[] = 'Installed FleekDash plugin directory was not found.';
        }
        if (!$checks['download_url_resolved']) {
            $warnings[] = 'Could not resolve a download URL for the requested version.';
        }
        if ($current_version === null || $current_version === '') {
            $warnings[] = 'Current installed FleekDash version could not be determined.';
        }
        if (!$checks['target_is_older']) {
            $warnings[] = 'Requested version must be older than the current installed version.';
        }
        if (!$checks['plugin_directory_writable']) {
            $warnings[] = 'Plugin directory is not writable by WordPress.';
        }
        if ($backup && !$checks['zip_archive_available']) {
            $warnings[] = 'ZipArchive is not available, so backup creation would fail.';
        }
        if ($backup && !$checks['backup_directory_ready']) {
            $warnings[] = 'Backup directory is not writable or cannot be created.';
        }
        if ($version === '') {
            $warnings[] = 'Target rollback version is empty.';
        }

        return $warnings;
    }

    private function getWordPressOrgPluginVersions(string $slug): array
    {
        $versions = array();
        if (!\function_exists('plugins_api')) {
            require_once \ABSPATH . self::REL_WP_ADMIN_PLUGIN_INSTALL;
        }

        $api_response = \plugins_api(
            'plugin_information',
            array(
                'slug' => $slug,
                'fields' => array(
                    'versions' => true,
                    'name' => true,
                ),
            ),
        );

        if (!\is_wp_error($api_response)) {
            if (\is_array($api_response)) {
                $api_response = (object) $api_response;
            }

            if (isset($api_response->versions) && !empty($api_response->versions)) {
                $versions_data = (array) $api_response->versions;
                foreach ($versions_data as $version => $download_url) {
                    $versions[] = array(
                        'version' => $version,
                        'download_url' => $download_url,
                        'size' => '',
                    );
                }

                \usort($versions, function ($a, $b) {
                    return \version_compare($b['version'], $a['version']);
                });

                $versions = \array_slice($versions, 0, 50);
            }
        }

        return $versions;
    }

    /**
     * Get theme versions from WordPress.org
     */
    private function getThemeVersions(string $slug)
    {
        $url = "https://api.wordpress.org/themes/info/1.2/?action=theme_information&request[slug]={$slug}&request[fields][versions]=true";

        $response = \wp_remote_get($url, array('timeout' => 15));

        if (\is_wp_error($response)) {
            return array();
        }

        $body = \wp_remote_retrieve_body($response);
        $data = \json_decode($body, true);

        if (empty($data) || !isset($data['versions'])) {
            return array();
        }

        $versions = array();

        foreach ($data['versions'] as $version => $download_url) {
            $versions[] = array(
                'version' => $version,
                'download_url' => $download_url,
                'size' => '',
            );
        }

        \usort($versions, function ($a, $b) {
            return \version_compare($b['version'], $a['version']);
        });

        return \array_slice($versions, 0, 50);
    }

    /**
     * Get current installed version
     *
     * @return string|null
     */
    private function getCurrentVersion(string $type, string $slug)
    {
        if ($type === 'plugin') {
            return $this->getCurrentPluginVersion($slug);
        }

        return $this->getCurrentThemeVersion($slug);
    }

    /**
     * @return string|null
     */
    private function getCurrentPluginVersion(string $slug)
    {
        if (!\function_exists('get_plugins')) {
            PluginHelper::ensurePluginApiLoaded();
        }

        $all_plugins = \get_plugins();

        foreach ($all_plugins as $plugin_file => $plugin_data) {
            $plugin_slug = \dirname($plugin_file);
            if ($plugin_slug === '.' || $plugin_slug === '') {
                $plugin_slug = \basename($plugin_file, '.php');
            }

            if ($plugin_slug === $slug) {
                return $plugin_data['Version'];
            }
        }

        return null;
    }

    /**
     * @return string|null
     */
    private function getCurrentThemeVersion(string $slug)
    {
        $theme = \wp_get_theme($slug);
        if ($theme->exists()) {
            return $theme->get('Version');
        }

        return null;
    }
}
