<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;

class RollbackUpgrade extends Command
{
    protected $signature = 'upgrade:rollback
                            {package : 要回滚的升级包名称（如 cmspro-5.0.0-to-5.1.0）}
                            {--skip-migrate : 跳过数据库回滚}
                            {--dry-run : 仅输出回滚计划，不实际执行}';

    protected $description = '回滚已部署的升级包（cmspro格式）';

    private string $codeRoot;
    private string $upgradeRoot;

    public function handle(): int
    {
        $this->codeRoot = base_path();
        $this->upgradeRoot = storage_path('upgrades');

        $packageArg = $this->argument('package');
        $dryRun = $this->option('dry-run');

        $packageDir = $this->resolvePackageDir($packageArg);
        $upgradeJsonPath = $packageDir . '/upgrade.json';

        if (!File::exists($upgradeJsonPath)) {
            $this->error("upgrade.json 不存在：{$upgradeJsonPath}");
            return self::FAILURE;
        }

        $manifest = json_decode(File::get($upgradeJsonPath), true);

        $this->info("=== 回滚升级包 ===");
        $this->info("当前版本：{$manifest['to_version']}");
        $this->info("回滚到：{$manifest['from_version']}");
        $this->newLine();

        $backupFile = $this->findBackupFile($manifest['to_version']);

        if ($backupFile) {
            $this->info("找到备份文件：" . basename($backupFile));
        } else {
            $this->warn('未找到备份文件，将仅删除新增文件和尝试回滚数据库。');
        }

        if ($dryRun) {
            $this->displayRollbackPlan($manifest, $backupFile);
            return self::SUCCESS;
        }

        if (!$this->confirm('确认执行回滚？此操作可能影响数据完整性。')) {
            $this->info('已取消。');
            return self::SUCCESS;
        }

        if (!$this->option('skip-migrate')) {
            $this->executeMigrateRollback($manifest);
        }

        if ($backupFile) {
            $this->restoreFromBackup($backupFile);
        } else {
            $this->warn('无备份文件，部分修改/删除的文件无法自动恢复。');
        }

        $this->removeAddedFiles($manifest);
        $this->call('optimize:clear');
        $this->call('config:cache');
        $this->call('route:cache');
        $this->call('view:cache');

        $this->info("回滚完成：v{$manifest['to_version']} → v{$manifest['from_version']}");
        return self::SUCCESS;
    }

    private function resolvePackageDir(string $packageArg): string
    {
        if (File::isDirectory($packageArg) && File::exists($packageArg . '/upgrade.json')) {
            return realpath($packageArg);
        }

        $inUpgrades = $this->upgradeRoot . '/' . $packageArg;
        if (File::isDirectory($inUpgrades) && File::exists($inUpgrades . '/upgrade.json')) {
            return realpath($inUpgrades);
        }

        return $packageArg;
    }

    private function displayRollbackPlan(array $manifest, ?string $backupFile): void
    {
        $this->info('--- 回滚计划 ---');
        $this->info("目标版本：{$manifest['from_version']}");

        if (!empty($manifest['files']['add'])) {
            $this->info("将删除新增文件：" . count($manifest['files']['add']) . " 个");
        }
        if (!empty($manifest['files']['modify'])) {
            $this->info("将恢复修改文件：" . count($manifest['files']['modify']) . " 个");
        }
        if (!empty($manifest['files']['delete'])) {
            $this->info("将恢复删除文件：" . count($manifest['files']['delete']) . " 个");
        }
        if (!empty($manifest['migrations'])) {
            $this->info("将回滚迁移：" . count($manifest['migrations']) . " 个");
        }

        if ($backupFile) {
            $this->info('备份文件可用，可恢复 modify/delete 的文件');
        } else {
            $this->warn('无备份文件，modify/delete 文件可能无法恢复');
        }
    }

    private function findBackupFile(string $version): ?string
    {
        $backupDir = $this->upgradeRoot . '/backups';
        if (!File::isDirectory($backupDir)) return null;

        $backups = collect(File::files($backupDir))
            ->filter(fn($file) => str_starts_with($file->getFilename(), "pre_{$version}_"))
            ->sortByDesc(fn($file) => $file->getMTime())
            ->values();

        return $backups->first()?->getRealPath();
    }

    private function executeMigrateRollback(array $manifest): void
    {
        $migrations = $manifest['migrations'] ?? [];
        if (empty($migrations)) {
            $this->info('无需回滚数据库迁移');
            return;
        }

        $stepCount = count($migrations);
        $this->info("回滚 {$stepCount} 个数据库迁移...");

        $this->call('migrate:rollback', ['--step' => $stepCount, '--force' => true]);
    }

    private function restoreFromBackup(string $backupFile): void
    {
        $backupData = json_decode(File::get($backupFile), true);
        $restored = 0;

        foreach ($backupData['files'] ?? [] as $relativePath => $content) {
            $destPath = $this->codeRoot . '/' . $relativePath;
            File::ensureDirectoryExists(dirname($destPath));
            File::put($destPath, base64_decode($content));
            $restored++;
        }

        $this->info("已从备份恢复 {$restored} 个文件");
    }

    private function removeAddedFiles(array $manifest): void
    {
        $addedFiles = $manifest['files']['add'] ?? [];
        $removed = 0;

        foreach ($addedFiles as $relativePath) {
            $fullPath = $this->codeRoot . '/' . $relativePath;
            if (File::exists($fullPath)) {
                File::delete($fullPath);
                $removed++;
                $this->line("已删除：{$relativePath}");
            }
        }

        if ($removed > 0) {
            $this->info("已删除 {$removed} 个新增文件");
        }
    }
}
