<?php

declare(strict_types=1);

namespace App\Framework\Database\Backup\Console;

use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ExitCode;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Database\Backup\BackupOptions;
use App\Framework\Database\Backup\BackupRetentionPolicy;
use App\Framework\Database\Backup\DatabaseBackupService;
use App\Framework\Filesystem\ValueObjects\FilePath;

/**
 * Console commands for database backup management
 */
final readonly class BackupCommand
{
    public function __construct(
        private DatabaseBackupService $backupService
    ) {
    }

    #[ConsoleCommand('backup:create', 'Create a database backup')]
    public function create(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $output->writeLine('🗄️  Creating database backup...', ConsoleColor::CYAN);
        $output->newLine();

        try {
            // Parse options
            $options = $this->parseBackupOptions($input);

            // Show backup options
            $this->displayBackupOptions($output, $options);

            // Create backup
            $result = $this->backupService->createBackup($options);

            if ($result->success) {
                $output->writeSuccess('✅ Backup created successfully!');
                $output->newLine();

                if ($result->metadata) {
                    $output->writeLine('📊 Backup Details:', ConsoleColor::CYAN);
                    $output->writeLine("• File: {$result->metadata->filename}");
                    $output->writeLine("• Size: {$result->metadata->getSummary()}");
                    $output->writeLine("• Original: {$result->metadata->originalSize->toHumanReadable()}");

                    if ($result->metadata->compressed) {
                        $ratio = round($result->metadata->getCompressionRatio(), 1);
                        $saved = $result->metadata->getSpaceSaved();
                        $output->writeLine("• Saved: {$saved->toHumanReadable()} ({$ratio}% compression)");
                    }

                    $output->writeLine("• Checksum: {$result->metadata->checksum}");
                }

                return ExitCode::SUCCESS;
            } else {
                $output->writeError('❌ Backup failed: ' . $result->message);

                return ExitCode::GENERAL_ERROR;
            }
        } catch (\Throwable $e) {
            $output->writeError('❌ Backup failed: ' . $e->getMessage());

            return ExitCode::GENERAL_ERROR;
        }
    }

    #[ConsoleCommand('backup:restore', 'Restore database from backup')]
    public function restore(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $backupFile = $input->getArgument(0);

        if (! $backupFile) {
            $backups = $this->backupService->listBackups();

            if (empty($backups)) {
                $output->writeError('❌ No backups found');

                return ExitCode::GENERAL_ERROR;
            }

            $output->writeLine('📋 Available backups:', ConsoleColor::CYAN);
            foreach ($backups as $i => $backup) {
                $output->writeLine(sprintf(
                    '%d. %s (%s) - %s',
                    $i + 1,
                    $backup['file'],
                    $backup['metadata']?->getSummary() ?? Byte::fromBytes($backup['size'])->toHumanReadable(),
                    $backup['created']
                ));
            }

            $choice = $output->askQuestion('Select backup number to restore:');
            $index = (int) $choice - 1;

            if (! isset($backups[$index])) {
                $output->writeError('❌ Invalid backup selection');

                return ExitCode::GENERAL_ERROR;
            }

            $backupFile = $backups[$index]['path'];
        }

        $backupPath = FilePath::create($backupFile);
        if (! $backupPath->exists()) {
            $output->writeError("❌ Backup file not found: {$backupFile}");

            return ExitCode::GENERAL_ERROR;
        }

        // Confirm restore
        $output->writeWarning('⚠️  This will overwrite the current database!');
        if (! $output->confirm('Are you sure you want to restore?', false)) {
            $output->writeInfo('Restore cancelled');

            return ExitCode::SUCCESS;
        }

        try {
            $output->writeLine('🔄 Restoring database from backup...', ConsoleColor::CYAN);

            $result = $this->backupService->restoreBackup($backupFile);

            if ($result->success) {
                $output->writeSuccess('✅ Database restored successfully!');

                return ExitCode::SUCCESS;
            } else {
                $output->writeError('❌ Restore failed: ' . $result->message);

                return ExitCode::GENERAL_ERROR;
            }
        } catch (\Throwable $e) {
            $output->writeError('❌ Restore failed: ' . $e->getMessage());

            return ExitCode::GENERAL_ERROR;
        }
    }

    #[ConsoleCommand('backup:list', 'List all available backups')]
    public function list(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        try {
            $backups = $this->backupService->listBackups();

            if (empty($backups)) {
                $output->writeInfo('ℹ️  No backups found');

                return ExitCode::SUCCESS;
            }

            $output->writeLine('📋 Available backups:', ConsoleColor::CYAN);
            $output->newLine();

            foreach ($backups as $backup) {
                $metadata = $backup['metadata'];
                $output->writeLine("📁 {$backup['file']}", ConsoleColor::WHITE);
                $output->writeLine("   Created: {$backup['created']}", ConsoleColor::GRAY);

                if ($metadata) {
                    $output->writeLine("   Size: {$metadata->getSummary()}", ConsoleColor::GRAY);
                    $output->writeLine("   Checksum: {$metadata->checksum}", ConsoleColor::GRAY);
                } else {
                    $fileSize = Byte::fromBytes($backup['size']);
                    $output->writeLine("   Size: {$fileSize->toHumanReadable()}", ConsoleColor::GRAY);
                }

                $output->newLine();
            }

            $output->writeLine("Total backups: " . count($backups), ConsoleColor::CYAN);

            return ExitCode::SUCCESS;
        } catch (\Throwable $e) {
            $output->writeError('❌ Failed to list backups: ' . $e->getMessage());

            return ExitCode::GENERAL_ERROR;
        }
    }

    #[ConsoleCommand('backup:cleanup', 'Clean up old backups based on retention policy')]
    public function cleanup(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        try {
            // Parse retention policy
            $maxDays = (int) $input->getOption('days', 30);
            $maxCount = (int) $input->getOption('count', 10);

            $policy = new BackupRetentionPolicy(
                maxAgeDays: $maxDays,
                maxCount: $maxCount
            );

            $output->writeLine('🧹 Cleaning up old backups...', ConsoleColor::CYAN);
            $output->writeLine("Policy: Keep {$maxCount} backups, max {$maxDays} days old");
            $output->newLine();

            $deletedCount = $this->backupService->cleanupBackups($policy);

            if ($deletedCount > 0) {
                $output->writeSuccess("✅ Deleted {$deletedCount} old backup(s)");
            } else {
                $output->writeInfo('ℹ️  No backups needed cleanup');
            }

            return ExitCode::SUCCESS;
        } catch (\Throwable $e) {
            $output->writeError('❌ Cleanup failed: ' . $e->getMessage());

            return ExitCode::GENERAL_ERROR;
        }
    }

    #[ConsoleCommand('backup:verify', 'Verify backup integrity')]
    public function verify(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $backupFile = $input->getArgument(0);

        if (! $backupFile) {
            $output->writeError('❌ Please specify a backup file to verify');

            return ExitCode::GENERAL_ERROR;
        }

        $backupPath = FilePath::create($backupFile);
        if (! $backupPath->exists()) {
            $output->writeError("❌ Backup file not found: {$backupFile}");

            return ExitCode::GENERAL_ERROR;
        }

        try {
            $output->writeLine('🔍 Verifying backup integrity...', ConsoleColor::CYAN);

            // Load metadata
            $metadataPath = $backupPath->withExtension($backupPath->getExtension() . '.meta');
            if ($metadataPath->exists()) {
                $jsonData = file_get_contents($metadataPath->toString());
                $metadataArray = json_decode($jsonData, true);

                if ($metadataArray) {
                    $metadata = \App\Framework\Database\Backup\BackupMetadata::fromArray($metadataArray);

                    // Verify checksum
                    $currentChecksum = hash_file('sha256', $backupPath->toString());

                    if ($currentChecksum === $metadata->checksum) {
                        $output->writeSuccess('✅ Backup integrity verified');
                        $output->writeLine("• File: {$metadata->filename}");
                        $output->writeLine("• Size: {$metadata->getSummary()}");
                        $output->writeLine("• Created: {$metadata->createdAt}");

                        return ExitCode::SUCCESS;
                    } else {
                        $output->writeError('❌ Backup integrity check failed - checksum mismatch');

                        return ExitCode::GENERAL_ERROR;
                    }
                } else {
                    $output->writeWarning('⚠️  Could not parse metadata file');
                }
            } else {
                $output->writeWarning('⚠️  No metadata file found - cannot verify integrity');
            }

            // Basic file check
            $size = $backupPath->getSize();
            if ($size->isNotEmpty()) {
                $output->writeInfo("ℹ️  File exists and has size: {$size->toHumanReadable()}");

                return ExitCode::SUCCESS;
            } else {
                $output->writeError('❌ Backup file is empty');

                return ExitCode::GENERAL_ERROR;
            }
        } catch (\Throwable $e) {
            $output->writeError('❌ Verification failed: ' . $e->getMessage());

            return ExitCode::GENERAL_ERROR;
        }
    }

    /**
     * Parse backup options from console input
     */
    private function parseBackupOptions(ConsoleInput $input): BackupOptions
    {
        $structureOnly = $input->hasOption('structure-only');
        $dataOnly = $input->hasOption('data-only');
        $encrypt = $input->hasOption('encrypt');
        $noCompress = $input->hasOption('no-compress');

        if ($structureOnly) {
            return BackupOptions::structureOnly();
        }

        if ($dataOnly) {
            return BackupOptions::dataOnly();
        }

        $options = new BackupOptions(
            compress: ! $noCompress,
            encrypt: $encrypt
        );

        return $encrypt ? $options->withEncryption() : $options;
    }

    /**
     * Display backup options
     */
    private function displayBackupOptions(ConsoleOutput $output, BackupOptions $options): void
    {
        $output->writeLine('⚙️  Backup Options:', ConsoleColor::CYAN);
        $output->writeLine("• Structure: " . ($options->includeStructure ? 'Yes' : 'No'));
        $output->writeLine("• Data: " . ($options->includeData ? 'Yes' : 'No'));
        $output->writeLine("• Compression: " . ($options->compress ? 'Yes' : 'No'));
        $output->writeLine("• Encryption: " . ($options->encrypt ? 'Yes' : 'No'));
        $output->newLine();
    }
}
