<?php

declare(strict_types=1);

namespace ShieldX\WPCLI;

defined('ABSPATH') || exit;

use ShieldX\Maintenance\DatabaseBackupManager;
use ShieldX\Maintenance\RestorePointManager;

final class DatabaseCommand
{
    /**
     * Create, list, or restore SHIELD-X database backups and restore points.
     *
     * @synopsis [<backup|backups|list|restore|restore-point|restore-points|restore-point-restore|restore-point-manual>] [<id>] [--tables=<tables>] [--max-rows=<rows>] [--limit=<limit>] [--reason=<reason>] [--paths=<paths>] [--skip-db] [--source=<auto|local|google>] [--yes] [--format=<format>]
     *
     * @param array<int,string>   $args
     * @param array<string,mixed> $assocArgs
     */
    public function __invoke(array $args, array $assocArgs): void
    {
        AdminGate::requireManageOptions();

        $subcommand = isset($args[0]) ? (string) $args[0] : 'backup';
        $json = CommandOutput::isJson($assocArgs);

        switch ($subcommand) {
            case 'backup':
                $this->backup($assocArgs, $json);
                return;

            case 'backups':
            case 'list':
                $this->listBackups($assocArgs, $json);
                return;

            case 'restore':
                $this->restore($args, $assocArgs, $json);
                return;

            case 'restore-point':
                $this->restorePoint($assocArgs, $json);
                return;

            case 'restore-points':
                $this->listRestorePoints($assocArgs, $json);
                return;

            case 'restore-point-restore':
                $this->restoreRestorePoint($args, $assocArgs, $json);
                return;

            case 'restore-point-manual':
                $this->restoreRestorePointManual($args, $assocArgs, $json);
                return;

            default:
                \WP_CLI::error('Unknown db command. Use backup, backups, restore, restore-point, restore-points, restore-point-restore, or restore-point-manual.');
        }
    }

    /**
     * @param array<string,mixed> $assocArgs
     */
    private function backup(array $assocArgs, bool $json): void
    {
        $tables = isset($assocArgs['tables'])
            ? array_values(array_filter(array_map('trim', explode(',', (string) $assocArgs['tables']))))
            : [];
        $maxRows = isset($assocArgs['max-rows']) ? (int) $assocArgs['max-rows'] : 5000;
        $result = DatabaseBackupManager::fromWordPress()->create($tables, $maxRows);

        if (empty($result['ok'])) {
            \WP_CLI::error((string) ($result['message'] ?? 'Database backup failed.'));
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'backup',
                'result' => $result,
            ]);
            return;
        }

        \WP_CLI::line('Backup ID: ' . (string) ($result['backup_id'] ?? ''));
        \WP_CLI::line('Tables: ' . (string) ($result['tables'] ?? 0));
        \WP_CLI::line('Rows: ' . (string) ($result['rows_exported'] ?? 0));
        \WP_CLI::line('Path: ' . (string) ($result['path'] ?? ''));
        \WP_CLI::success('SHIELD-X database backup created.');
    }

    /**
     * @param array<string,mixed> $assocArgs
     */
    private function listBackups(array $assocArgs, bool $json): void
    {
        $limit = isset($assocArgs['limit']) ? (int) $assocArgs['limit'] : 8;
        $backups = DatabaseBackupManager::fromWordPress()->recent($limit);
        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'backups',
                'limit' => $limit,
                'count' => count($backups),
                'backups' => $backups,
            ]);
            return;
        }

        if (empty($backups)) {
            \WP_CLI::line('No manual database backups found.');
            return;
        }

        foreach ($backups as $backup) {
            \WP_CLI::line(sprintf(
                '%s %s tables=%d rows=%d size=%d',
                (string) ($backup['backup_id'] ?? ''),
                (string) ($backup['created_at'] ?? ''),
                (int) ($backup['table_count'] ?? 0),
                (int) ($backup['rows_exported'] ?? 0),
                (int) ($backup['size'] ?? 0)
            ));
        }
        \WP_CLI::success('SHIELD-X database backups listed.');
    }

    /**
     * @param array<int,string> $args
     * @param array<string,mixed> $assocArgs
     */
    private function restore(array $args, array $assocArgs, bool $json): void
    {
        AdminGate::requireDestructiveDatabaseRestore();

        $backupId = isset($args[1]) ? (string) $args[1] : '';
        if ($backupId === '') {
            \WP_CLI::error('Backup ID is required.');
        }

        \WP_CLI::confirm('Restore this SHIELD-X database backup into the live WordPress database?', $assocArgs);
        $result = DatabaseBackupManager::fromWordPress()->restore($backupId);
        if (empty($result['ok'])) {
            \WP_CLI::error((string) ($result['message'] ?? 'Database restore failed.'));
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'restore',
                'result' => $result,
            ]);
            return;
        }

        \WP_CLI::line('Backup ID: ' . (string) ($result['backup_id'] ?? $backupId));
        \WP_CLI::line('Tables: ' . (string) ($result['tables'] ?? 0));
        \WP_CLI::line('Rows restored: ' . (string) ($result['rows_restored'] ?? 0));
        \WP_CLI::success('SHIELD-X database backup restored.');
    }

    /**
     * @param array<string,mixed> $assocArgs
     */
    private function restorePoint(array $assocArgs, bool $json): void
    {
        $reason = isset($assocArgs['reason']) ? (string) $assocArgs['reason'] : 'cli';
        $paths = isset($assocArgs['paths'])
            ? array_values(array_filter(array_map('trim', explode(',', (string) $assocArgs['paths']))))
            : [];
        $includeDb = !isset($assocArgs['skip-db']);
        $result = RestorePointManager::fromWordPress()->create($reason, $paths, 0, $includeDb);

        if (empty($result['ok'])) {
            \WP_CLI::error((string) ($result['message'] ?? 'Restore point failed.'));
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'restore-point',
                'result' => $result,
            ]);
            return;
        }

        \WP_CLI::line('Restore ID: ' . (string) ($result['restore_id'] ?? ''));
        \WP_CLI::line('Files: ' . (string) ($result['files'] ?? 0));
        \WP_CLI::line('Database backup: ' . (string) ($result['database_backup_id'] ?? ''));
        \WP_CLI::line('Path: ' . (string) ($result['path'] ?? ''));
        \WP_CLI::success('SHIELD-X restore point created.');
    }

    /**
     * @param array<string,mixed> $assocArgs
     */
    private function listRestorePoints(array $assocArgs, bool $json): void
    {
        $limit = isset($assocArgs['limit']) ? (int) $assocArgs['limit'] : 8;
        $items = RestorePointManager::fromWordPress()->recent($limit);
        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'restore-points',
                'limit' => $limit,
                'count' => count($items),
                'restore_points' => $items,
            ]);
            return;
        }

        if (empty($items)) {
            \WP_CLI::line('No restore points found.');
            return;
        }

        foreach ($items as $item) {
            \WP_CLI::line(sprintf(
                '%s %s reason=%s files=%d db=%s',
                (string) ($item['restore_id'] ?? ''),
                (string) ($item['created_at'] ?? ''),
                (string) ($item['reason'] ?? ''),
                (int) ($item['files_count'] ?? 0),
                (string) ($item['database_backup_id'] ?? '')
            ));
        }
        \WP_CLI::success('SHIELD-X restore points listed.');
    }

    /**
     * @param array<int,string> $args
     * @param array<string,mixed> $assocArgs
     */
    private function restoreRestorePoint(array $args, array $assocArgs, bool $json): void
    {
        AdminGate::requireDestructiveDatabaseRestore();

        $restoreId = isset($args[1]) ? (string) $args[1] : '';
        if ($restoreId === '') {
            \WP_CLI::error('Restore point ID is required.');
        }

        \WP_CLI::confirm('Restore this SHIELD-X restore point into the live WordPress site?', $assocArgs);
        if (!class_exists('\ShieldXRecovery\IsolatedRestoreCoordinator')) {
            \WP_CLI::error('SHIELD-X Recovery must be active before a full-site restore can begin.');
        }

        $coordinator = \ShieldXRecovery\IsolatedRestoreCoordinator::fromWordPress();
        $start = $coordinator->start(
            $restoreId,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
            isset($assocArgs['source']) ? (string) $assocArgs['source'] : 'auto'
        );
        if (empty($start['ok'])) {
            \WP_CLI::error((string) ($start['message'] ?? 'Isolated full restore could not start.'));
        }

        $jobId = (string) ($start['job_id'] ?? '');
        unset($start['browser_token']);
        if (isset($assocArgs['detach'])) {
            if ($json) {
                CommandOutput::writeJson([
                    'command' => 'db',
                    'subcommand' => 'restore-point-restore',
                    'detached' => true,
                    'result' => $start,
                ]);
                return;
            }

            \WP_CLI::line('Restore job: ' . $jobId);
            \WP_CLI::line('Status: ' . (string) ($start['status'] ?? 'running'));
            \WP_CLI::success('Isolated full restore started in detached mode.');
            return;
        }

        $result = $coordinator->runToCompletion(
            $jobId,
            isset($assocArgs['batch-size']) ? max(1, (int) $assocArgs['batch-size']) : 75
        );
        if (empty($result['ok']) || (string) ($result['status'] ?? '') !== 'completed') {
            \WP_CLI::warning('Restore job ' . $jobId . ' remains available for an explicit resume or rollback.');
            \WP_CLI::error((string) ($result['last_error'] ?? $result['message'] ?? 'Isolated full restore stopped.'));
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'restore-point-restore',
                'result' => $result,
            ]);
            return;
        }

        \WP_CLI::line('Restore ID: ' . (string) ($result['restore_id'] ?? $restoreId));
        \WP_CLI::line('Restore job: ' . $jobId);
        \WP_CLI::line('Files published: ' . (string) ($result['files_published'] ?? 0));
        \WP_CLI::line('Database swapped: ' . (!empty($result['database_swapped']) ? 'yes' : 'no'));
        \WP_CLI::success('SHIELD-X isolated full restore completed and passed its health check.');
    }

    /**
     * Run the deliberately manual core restore route. It is available without
     * SHIELD-X Recovery but cannot provide the add-on's isolated resume or
     * rollback guarantees.
     *
     * @param array<int,string> $args
     * @param array<string,mixed> $assocArgs
     */
    private function restoreRestorePointManual(array $args, array $assocArgs, bool $json): void
    {
        AdminGate::requireDestructiveDatabaseRestore();

        $restoreId = isset($args[1]) ? (string) $args[1] : '';
        if ($restoreId === '') {
            \WP_CLI::error('Restore point ID is required. Use "wp shield-x db restore-points --user=<administrator>" to list available restore points.');
        }

        $confirmationError = ManualRestoreConfirmation::error($assocArgs, ManualRestoreConfirmation::DANGER_PHRASE);
        if ($confirmationError !== '') {
            \WP_CLI::error($confirmationError);
        }

        $restoreSource = isset($assocArgs['source']) ? strtolower(trim((string) $assocArgs['source'])) : 'auto';
        if (!in_array($restoreSource, ['auto', 'local', 'google'], true)) {
            \WP_CLI::error('Restore source must be auto, local, or google.');
        }

        $restorePoints = RestorePointManager::fromWordPress();
        $summary = $restorePoints->manualRestoreSummary($restoreId, $restoreSource);
        if (empty($summary['ok'])) {
            \WP_CLI::error((string) ($summary['message'] ?? 'Manual full restore preflight failed.'));
        }

        $this->printManualRestoreSummary($summary);
        \WP_CLI::line('This core-only restore has no resume or rollback. SHIELD-X and recovery runtime files will be preserved.');
        \WP_CLI::line('Type "' . ManualRestoreConfirmation::DANGER_PHRASE . '" to restore this live site:');
        $providedPhrase = defined('STDIN') ? fgets(STDIN, 256) : false;
        $confirmationError = ManualRestoreConfirmation::error($assocArgs, is_string($providedPhrase) ? $providedPhrase : '');
        if ($confirmationError !== '') {
            \WP_CLI::error($confirmationError);
        }

        $result = $restorePoints->restoreManualFullSite(
            $restoreId,
            function_exists('get_current_user_id') ? (int) get_current_user_id() : 0,
            $restoreSource
        );
        if (empty($result['ok'])) {
            \WP_CLI::error((string) ($result['message'] ?? 'Manual full restore failed.'));
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'db',
                'subcommand' => 'restore-point-manual',
                'result' => $result,
            ]);
            return;
        }

        \WP_CLI::line('Restore ID: ' . (string) ($result['restore_id'] ?? $restoreId));
        \WP_CLI::line('Files restored: ' . (string) ($result['files_restored'] ?? 0));
        \WP_CLI::line('Files removed: ' . (string) ($result['files_removed'] ?? 0));
        \WP_CLI::line('Protected files preserved: ' . (string) ($result['protected_files_skipped'] ?? 0));
        \WP_CLI::line('Database restored: ' . (!empty($result['database']['skipped']) ? 'no' : 'yes'));
        \WP_CLI::success('SHIELD-X manual full restore completed. Verify the site before making further changes.');
    }

    /**
     * @param array<string,mixed> $summary
     */
    private function printManualRestoreSummary(array $summary): void
    {
        $database = isset($summary['database']) && is_array($summary['database']) ? $summary['database'] : [];
        \WP_CLI::line('Restore ID: ' . (string) ($summary['restore_id'] ?? ''));
        \WP_CLI::line('Created: ' . (string) ($summary['created_at'] ?? 'unknown'));
        \WP_CLI::line('Reason: ' . (string) ($summary['reason'] ?? 'manual'));
        \WP_CLI::line('Snapshot: ' . (string) ($summary['snapshot_mode'] ?? 'full'));
        \WP_CLI::line('Files to restore: ' . (string) ($summary['files_total'] ?? 0));
        \WP_CLI::line('Protected files preserved: ' . (string) ($summary['protected_files'] ?? 0));
        \WP_CLI::line(
            !empty($database['included'])
                ? 'Database: ' . (string) ($database['tables'] ?? 0) . ' tables from ' . (string) ($database['backup_id'] ?? '')
                : 'Database: not included in this restore point'
        );
    }
}
