<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Process\Process;

class BackupRestoreLatestCommand extends Command
{
    protected $signature = 'backup:restore-latest
        {--url= : Base URL server cloud backup}
        {--token= : Token API cloud backup}
        {--name= : Nama backup}
        {--output= : Lokasi file download}
        {--file= : Pakai file backup lokal, tanpa download}
        {--download-only : Hanya download, tidak restore}
        {--host= : Host PostgreSQL target}
        {--port= : Port PostgreSQL target}
        {--database= : Database PostgreSQL target}
        {--username= : Username PostgreSQL target}
        {--password= : Password PostgreSQL target, opsional jika .pgpass sudah ada}
        {--clean=1 : Jalankan pg_restore --clean}
        {--if-exists=1 : Jalankan pg_restore --if-exists}
        {--no-owner=1 : Jalankan pg_restore --no-owner}
        {--yes : Lewati konfirmasi restore}';

    protected $description = 'Download backup terbaru dari duidev.com dan restore penuh ke PostgreSQL.';

    public function handle(): int
    {
        $backupName = (string) ($this->option('name') ?: config('cloud_backup.backup_name', 'lismikro'));
        $file = $this->option('file')
            ? (string) $this->option('file')
            : $this->downloadLatest($backupName);

        if (!$file || !File::exists($file)) {
            $this->error('File backup tidak ditemukan.');
            return Command::FAILURE;
        }

        $this->info('File backup siap: ' . $file);

        if ($this->option('download-only')) {
            return Command::SUCCESS;
        }

        $host = (string) ($this->option('host') ?: config('cloud_backup.db_host'));
        $port = (string) ($this->option('port') ?: config('cloud_backup.db_port'));
        $database = (string) ($this->option('database') ?: config('cloud_backup.db_database'));
        $username = (string) ($this->option('username') ?: config('cloud_backup.db_username'));

        if (!$this->option('yes') && !$this->confirm("Restore penuh ke database {$database} di {$host}:{$port}?", false)) {
            $this->warn('Restore dibatalkan.');
            return Command::FAILURE;
        }

        $command = [
            (string) config('cloud_backup.pg_restore', 'pg_restore'),
            '-h', $host,
            '-p', $port,
            '-U', $username,
            '-d', $database,
            '-v',
        ];

        if ((int) $this->option('clean') === 1) {
            $command[] = '--clean';
        }

        if ((int) $this->option('if-exists') === 1) {
            $command[] = '--if-exists';
        }

        if ((int) $this->option('no-owner') === 1) {
            $command[] = '--no-owner';
        }

        $command[] = $file;

        $env = null;
        $password = (string) ($this->option('password') ?: config('cloud_backup.db_password'));
        if ($password !== '') {
            $env = ['PGPASSWORD' => $password];
        }

        $process = new Process($command, base_path(), $env, null, null);
        $process->setTimeout(null);

        $this->info('Menjalankan pg_restore...');
        $process->run(function (string $type, string $buffer): void {
            $this->output->write($buffer);
        });

        if (!$process->isSuccessful()) {
            $this->error('Restore gagal dengan kode: ' . $process->getExitCode());
            return Command::FAILURE;
        }

        $this->info('Restore selesai.');

        return Command::SUCCESS;
    }

    private function downloadLatest(string $backupName): ?string
    {
        $baseUrl = rtrim((string) ($this->option('url') ?: config('cloud_backup.api_url')), '/');
        $token = (string) ($this->option('token') ?: config('cloud_backup.api_token'));

        if ($baseUrl === '' || $token === '') {
            $this->error('CLOUD_BACKUP_API_URL dan CLOUD_BACKUP_API_TOKEN wajib diisi.');
            return null;
        }

        $output = $this->option('output')
            ? (string) $this->option('output')
            : (string) config('cloud_backup.download_path');

        File::ensureDirectoryExists(dirname($output), 0750, true);

        $metadata = Http::withToken($token)
            ->timeout(60)
            ->get($baseUrl . '/api/backups/latest', ['backup_name' => $backupName]);

        if (!$metadata->successful()) {
            $this->error('Gagal membaca metadata backup terbaru. HTTP ' . $metadata->status());
            $this->line($metadata->body());
            return null;
        }

        $info = $metadata->json();
        $this->info('Backup terbaru: ' . ($info['file'] ?? '-'));

        $download = Http::withToken($token)
            ->timeout(0)
            ->sink($output)
            ->get($baseUrl . '/api/backups/latest/download', ['backup_name' => $backupName]);

        if (!$download->successful()) {
            File::delete($output);
            $this->error('Gagal download backup. HTTP ' . $download->status());
            $this->line($download->body());
            return null;
        }

        if (!empty($info['sha256']) && hash_file('sha256', $output) !== strtolower((string) $info['sha256'])) {
            File::delete($output);
            $this->error('Checksum file download tidak sesuai.');
            return null;
        }

        return $output;
    }
}
