<?php

/**
 * ============================================================
 *
 * @project   biiproject
 *
 * @author    Andika Debi Putra
 *
 * @email     andikadebiputra@gmail.com
 *
 * @website   https://biiproject.com
 *
 * @copyright Copyright (c) 2026 Andika Debi Putra
 * @license   Proprietary - All Rights Reserved
 *
 * @version   1.0.0
 *
 * @created   2026-05-01
 * ============================================================
 *
 * Unauthorized copying, modification, distribution, or use
 * of this file is strictly prohibited without prior written
 * permission from the author.
 * ============================================================
 */

namespace App\Console\Commands;

use App\Models\User;
use App\Notifications\BackupFailedNotification;
use App\Services\Notification\TelegramService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class VerifyBackups extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'backups:verify
                            {--disk=local : Storage disk to check}
                            {--max-age=7 : Alert if newest backup is older than N days}
                            {--min-size=1024 : Alert if backup is smaller than N bytes}';

    /**
     * The description of the console command.
     *
     * @var string
     */
    protected $description = 'Verify backups exist, are recent, and have non-trivial file sizes';

    /**
     * Execute the console command.
     */
    public function handle(TelegramService $telegram): int
    {
        $disk = $this->option('disk');
        $maxAgeDays = (int) $this->option('max-age');
        $minBytes = (int) $this->option('min-size');

        $this->info("Verifying backups on disk: {$disk}");

        $storage = Storage::disk($disk);

        if (! $storage->exists('Laravel')) {
            $reason = 'Backup directory "Laravel" not found on disk.';
            $this->error($reason);
            $this->notifyFailure($reason, $disk, $telegram);

            return self::FAILURE;
        }

        $files = collect($storage->allFiles('Laravel'))
            ->filter(fn ($f) => Str::endsWith($f, ['.zip', '.gz', '.sql']))
            ->sortByDesc(fn ($f) => $storage->lastModified($f))
            ->values();

        if ($files->isEmpty()) {
            $reason = 'No backup files found.';
            $this->error($reason);
            $this->notifyFailure($reason, $disk, $telegram);

            return self::FAILURE;
        }

        $latest = $files->first();
        $latestModified = $storage->lastModified($latest);
        $latestSize = $storage->size($latest);
        $ageDays = (int) round((time() - $latestModified) / 86400);

        $this->table(['File', 'Age (days)', 'Size (KB)', 'Status'], [[
            basename($latest),
            $ageDays,
            round($latestSize / 1024, 1),
            $this->statusLabel($ageDays, $latestSize, $maxAgeDays, $minBytes),
        ]]);

        $problems = [];

        if ($ageDays > $maxAgeDays) {
            $problems[] = "Latest backup is {$ageDays} days old (threshold: {$maxAgeDays} days)";
        }

        if ($latestSize < $minBytes) {
            $problems[] = "Latest backup is suspiciously small ({$latestSize} bytes)";
        }

        if (! empty($problems)) {
            foreach ($problems as $problem) {
                $this->warn($problem);
            }
            $this->notifyFailure(implode('; ', $problems), $disk, $telegram);

            return self::FAILURE;
        }

        $this->info("✓ Backup verification passed. {$files->count()} backup(s) found.");
        Log::channel('single')->info('[VerifyBackups] OK', ['count' => $files->count(), 'age_days' => $ageDays]);

        return self::SUCCESS;
    }

    private function statusLabel(int $age, int $size, int $maxAge, int $minBytes): string
    {
        if ($age > $maxAge || $size < $minBytes) {
            return '⚠ WARN';
        }

        return '✓ OK';
    }

    private function notifyFailure(string $reason, string $disk, TelegramService $telegram): void
    {
        Log::channel('single')->error('[VerifyBackups] FAILED', compact('reason', 'disk'));

        // Prepare Telegram Alert
        $hostname = gethostname();
        $message = "🚨 <b>BACKUP VERIFICATION FAILED</b> 🚨\n";
        $message .= "--------------------------------\n";
        $message .= "<b>Host:</b> {$hostname}\n";
        $message .= "<b>Disk:</b> {$disk}\n";
        $message .= "<b>Reason:</b> {$reason}\n";
        $message .= "--------------------------------\n";
        $message .= '<i>Please check the backup logs immediately!</i>';

        $telegram->sendMessage($message);

        // Notify developer role via database notification
        try {
            $developers = User::role('Developer')->get();
            foreach ($developers as $dev) {
                $dev->notify(new BackupFailedNotification($reason));
            }
        } catch (\Throwable) {
            // Silently skip if notification classes don't exist yet
        }
    }
}
