<?php

/**
 * Version Manager
 *
 * Handles version rollback, backup creation, and version management
 *
 * @package ProRank\SEO\Core
 */

declare(strict_types=1);

namespace ProRank\SEO\Core;

use WP_Error;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}




class VersionManager {

    /**
     * Backup directory
     */
    private string $backup_dir;

    /**
     * Constructor
     */
    public function __construct() {
        $this->backup_dir = $this->resolve_backup_dir();
    }

    /**
     * Store backups outside the site document root so they are not directly
     * downloadable over HTTP.
     */
    private function resolve_backup_dir(): string {
        $site_root        = wp_normalize_path(untrailingslashit(ABSPATH));
        $site_parent_root = wp_normalize_path(dirname($site_root));
        $site_fingerprint = md5((string) home_url('/'));

        return trailingslashit($site_parent_root) . 'prorank-backups-' . $site_fingerprint . '/';
    }

    /**
     * Create a backup of the current plugin version
     *
     * @return array|WP_Error
     */
    public function create_backup() {
        $this->cleanup_legacy_public_backup_dir();

        // Ensure backup directory exists
        if (!wp_mkdir_p($this->backup_dir)) {
            return new WP_Error(
                'backup_dir_error',
                __('Failed to create backup directory', 'prorank-seo')
            );
        }

        // Create .htaccess to protect backups
        $htaccess_path = $this->backup_dir . '.htaccess';
        if (!file_exists($htaccess_path)) {
            file_put_contents($htaccess_path, 'Deny from all');
        }

        $index_path = $this->backup_dir . 'index.php';
        if (!file_exists($index_path)) {
            file_put_contents($index_path, "<?php\n// Silence is golden.\n");
        }

        $plugin_dir = PRORANK_PLUGIN_DIR;
        $backup_name = 'prorank-seo-' . PRORANK_VERSION . '-' . wp_date('Y-m-d-H-i-s') . '.zip';
        $backup_path = $this->backup_dir . $backup_name;

        // Create ZIP backup
        if (!class_exists('ZipArchive')) {
            return new WP_Error(
                'zip_not_available',
                __('PHP ZipArchive extension is required for backup', 'prorank-seo')
            );
        }

        $zip = new \ZipArchive();
        if ($zip->open($backup_path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
            return new WP_Error(
                'zip_create_error',
                __('Failed to create backup archive', 'prorank-seo')
            );
        }

        // Add plugin files to ZIP
        $files = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($plugin_dir, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        $exclude_dirs = ['node_modules', '.git', '.cache'];

        foreach ($files as $file) {
            $file_path = $file->getRealPath();
            $relative_path = str_replace($plugin_dir, '', $file_path);

            // Skip excluded directories
            $skip = false;
            foreach ($exclude_dirs as $exclude) {
                if (strpos($relative_path, DIRECTORY_SEPARATOR . $exclude . DIRECTORY_SEPARATOR) !== false ||
                    strpos($relative_path, $exclude . DIRECTORY_SEPARATOR) === 0) {
                    $skip = true;
                    break;
                }
            }

            if ($skip) {
                continue;
            }

            if ($file->isDir()) {
                $zip->addEmptyDir($relative_path);
            } else {
                $zip->addFile($file_path, $relative_path);
            }
        }

        $zip->close();

        // Save backup info
        $backups = get_option('prorank_backup_history', []);
        $backups[] = [
            'version' => PRORANK_VERSION,
            'file' => $backup_name,
            'created_at' => current_time('mysql'),
            'size' => filesize($backup_path),
        ];

        // Keep only last 5 backups
        if (count($backups) > 5) {
            $old_backups = array_slice($backups, 0, count($backups) - 5);
            foreach ($old_backups as $old) {
                $old_path = $this->backup_dir . $old['file'];
                if (file_exists($old_path)) {
                    \ProRank\SEO\Core\FilesystemHelper::delete_file($old_path);
                }
            }
            $backups = array_slice($backups, -5);
        }

        update_option('prorank_backup_history', $backups);

        return [
            'backup_path' => $backup_path,
            'backup_name' => $backup_name,
            'version' => PRORANK_VERSION,
        ];
    }

    /**
     * Get the latest available plugin backup.
     *
     * @return array<string, mixed>|null
     */
    public function get_latest_backup(): ?array {
        $backups = get_option('prorank_backup_history', []);

        if (!is_array($backups) || empty($backups)) {
            return null;
        }

        $latest = end($backups);
        if (!is_array($latest)) {
            return null;
        }

        $backup_name = isset($latest['file']) && is_string($latest['file']) ? $latest['file'] : '';
        if ($backup_name === '') {
            return null;
        }

        $resolved = $this->resolve_backup_file($backup_name);
        if (is_wp_error($resolved)) {
            return null;
        }

        return [
            'file'       => $resolved['name'],
            'path'       => $resolved['path'],
            'created_at' => isset($latest['created_at']) ? (string) $latest['created_at'] : '',
            'size'       => isset($latest['size']) ? (int) $latest['size'] : $resolved['size'],
            'version'    => isset($latest['version']) ? (string) $latest['version'] : '',
        ];
    }

    /**
     * Build a signed admin download URL for a backup archive.
     *
     * @param string $backup_name Backup filename.
     * @return string
     */
    public function get_backup_download_url(string $backup_name): string {
        return add_query_arg([
            'action'   => 'prorank_download_backup',
            'backup'   => $backup_name,
            '_wpnonce' => wp_create_nonce('prorank_download_backup_' . $backup_name),
        ], admin_url('admin-post.php'));
    }

    /**
     * Remove legacy backups that were stored under wp-content and therefore
     * were directly accessible on servers that do not honor .htaccess.
     */
    private function cleanup_legacy_public_backup_dir(): void {
        $legacy_dir = trailingslashit(WP_CONTENT_DIR) . 'prorank-backups';
        $current_dir = wp_normalize_path(untrailingslashit($this->backup_dir));
        $legacy_dir_normalized = wp_normalize_path(untrailingslashit($legacy_dir));

        if ($legacy_dir_normalized === $current_dir || !is_dir($legacy_dir)) {
            return;
        }

        \ProRank\SEO\Core\FilesystemHelper::delete_dir($legacy_dir, true);
    }

    /**
     * Resolve a requested backup file to a safe readable path.
     *
     * @param string $backup_name Requested backup filename.
     * @return array<string, mixed>|WP_Error
     */
    public function resolve_backup_file(string $backup_name = '') {
        $requested_backup = trim($backup_name);

        if ($requested_backup === '') {
            $latest = $this->get_latest_backup();
            if (!$latest || empty($latest['file'])) {
                return new WP_Error(
                    'backup_not_found',
                    __('No plugin backup is available to download.', 'prorank-seo')
                );
            }

            $requested_backup = (string) $latest['file'];
        }

        $requested_backup = wp_basename($requested_backup);
        if ($requested_backup === '' || !preg_match('/\A[a-zA-Z0-9._-]+\.zip\z/', $requested_backup)) {
            return new WP_Error(
                'backup_invalid',
                __('Invalid backup file requested.', 'prorank-seo')
            );
        }

        $backup_history = get_option('prorank_backup_history', []);
        $allowed_files  = [];

        if (is_array($backup_history)) {
            foreach ($backup_history as $backup) {
                if (is_array($backup) && !empty($backup['file']) && is_string($backup['file'])) {
                    $allowed_files[] = $backup['file'];
                }
            }
        }

        if (!in_array($requested_backup, $allowed_files, true)) {
            return new WP_Error(
                'backup_not_found',
                __('The requested backup could not be found in backup history.', 'prorank-seo')
            );
        }

        $backup_path = $this->backup_dir . $requested_backup;
        $real_path   = realpath($backup_path);
        $backup_root = realpath($this->backup_dir);

        if ($real_path === false || $backup_root === false) {
            return new WP_Error(
                'backup_missing',
                __('The requested backup file no longer exists on the server.', 'prorank-seo')
            );
        }

        $real_path   = wp_normalize_path($real_path);
        $backup_root = trailingslashit(wp_normalize_path($backup_root));

        if (strpos($real_path, $backup_root) !== 0) {
            return new WP_Error(
                'backup_invalid_location',
                __('The requested backup path is invalid.', 'prorank-seo')
            );
        }

        if (!is_file($real_path) || !is_readable($real_path)) {
            return new WP_Error(
                'backup_unreadable',
                __('The requested backup file is not readable.', 'prorank-seo')
            );
        }

        return [
            'path' => $real_path,
            'name' => $requested_backup,
            'size' => (int) filesize($real_path),
        ];
    }

    /**
     * Rollback to a specific version
     *
     * @param string $target_version Version to rollback to
     * @return array|WP_Error
     */
    public function rollback_to_version(string $target_version) {
        unset($target_version);

        return new WP_Error(
            'rollback_disabled',
            __('Rollback installation is not available in this build. Use the WordPress plugin updater or manually install an approved package.', 'prorank-seo')
        );
    }

    /**
     * Get rollback history
     *
     * @return array
     */
    public function get_rollback_history(): array {
        return get_option('prorank_rollback_history', []);
    }
}
