<?php

declare(strict_types=1);

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Symfony\Component\Process\Process;

/**
 * Restore a previous backup.
 *
 *  --date=YYYY-MM-DD[-H-i]   (required — picks newest archive matching prefix)
 *  --type=db|files|code      (default: db)
 *  --force                   (skip interactive confirmation)
 */
class RestoreCommand extends Command
{
    protected $signature = 'backup:restore {--date=} {--type=db} {--force}';

    protected $description = 'Restore the platform from a backup archive.';

    private const ROOT = '/www/backup';

    public function handle(): int
    {
        $date = (string) $this->option('date');
        $type = (string) $this->option('type');
        $force = (bool) $this->option('force');

        if ($date === '') {
            $this->error('--date=YYYY-MM-DD is required');

            return self::INVALID;
        }
        if (! in_array($type, ['db', 'files', 'code'], true)) {
            $this->error('--type must be db|files|code');

            return self::INVALID;
        }

        $dir = self::ROOT . "/easybox-{$type}";
        $ext = $type === 'db' ? '.sql.gz' : '.tar.gz';
        $candidates = glob("{$dir}/{$date}*{$ext}") ?: [];
        if (empty($candidates)) {
            $this->error("No backup found for {$date} in {$dir}");

            return self::FAILURE;
        }
        rsort($candidates);
        $archive = $candidates[0];

        $this->warn("Will restore from: {$archive}");
        if (! $force && ! $this->confirm('This is destructive. Proceed?', false)) {
            return self::FAILURE;
        }

        // Auto-create a safety backup first.
        $safety = Carbon::now()->format('Y-m-d-H-i') . '-pre-restore';
        $this->line("Creating safety backup ({$safety})…");
        $this->call('backup:run', ['--type' => $type, '--rotate' => 0]);

        if ($type === 'db') {
            return $this->restoreDb($archive);
        }

        return $this->restoreArchive($archive, $type);
    }

    private function restoreDb(string $archive): int
    {
        $db = (string) config('database.connections.mysql.database');
        $user = (string) config('database.connections.mysql.username');
        $pass = (string) config('database.connections.mysql.password');
        $host = (string) config('database.connections.mysql.host');
        $port = (string) config('database.connections.mysql.port');

        $cmd = sprintf(
            'gunzip < %s | mysql -h%s -P%s -u%s %s %s',
            escapeshellarg($archive),
            escapeshellarg($host),
            escapeshellarg($port),
            escapeshellarg($user),
            $pass !== '' ? '-p' . escapeshellarg($pass) : '',
            escapeshellarg($db),
        );

        $proc = Process::fromShellCommandline($cmd);
        $proc->setTimeout(3600);
        $proc->mustRun();

        $this->components->info('DB restored.');

        return self::SUCCESS;
    }

    private function restoreArchive(string $archive, string $type): int
    {
        $target = $type === 'code' ? dirname(base_path()) : base_path();
        $cmd = sprintf('tar xzf %s -C %s', escapeshellarg($archive), escapeshellarg($target));
        $proc = Process::fromShellCommandline($cmd);
        $proc->setTimeout(3600);
        $proc->mustRun();
        $this->components->info("Archive extracted to {$target}");

        return self::SUCCESS;
    }
}
