<?php

namespace App\Console\Commands;

use App\Models\Central\Company;
use App\Services\TenantBackupService;
use App\Tenancy\TenantManager;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;

class BackupAllTenantsCommand extends Command
{
    protected $signature = 'backup:tenants
                            {--keep=14 : How many daily backups to retain per tenant}
                            {--slug= : Backup only one tenant by slug}';

    protected $description = 'Export ZIP backup for every active tenant. Stored in storage/app/backups/{date}/';

    public function handle(TenantBackupService $service): int
    {
        $query = Company::query()->where('status', '!=', 'archived');
        if ($slug = $this->option('slug')) {
            $query->where('slug', $slug);
        }
        $companies = $query->get();

        if ($companies->isEmpty()) {
            $this->warn('No tenants to back up.');
            return self::SUCCESS;
        }

        $date = date('Y-m-d');
        $base = storage_path("app/backups/{$date}");
        if (! is_dir($base)) @mkdir($base, 0775, true);

        $manager = app(TenantManager::class);
        $ok = 0;
        $failed = 0;

        foreach ($companies as $company) {
            try {
                $manager->setCurrent($company);
                app(\Spatie\Permission\PermissionRegistrar::class)
                    ->setPermissionsTeamId($company->id);

                $tmp = $service->export($company);
                $dest = "{$base}/{$company->slug}.zip";
                @rename($tmp, $dest);

                $size = round(filesize($dest) / 1024, 1);
                $this->info("✓ {$company->slug} → {$size}KB");

                // Offsite copy to B2 (if configured) — disk lazily resolved.
                $this->uploadOffsite($dest, "{$date}/{$company->slug}.zip");
                $ok++;
            } catch (\Throwable $e) {
                $this->error("✗ {$company->slug}: {$e->getMessage()}");
                $failed++;
            } finally {
                $manager->setCurrent(null);
            }
        }

        $this->cleanupOld((int) $this->option('keep'));

        $this->newLine();
        $this->info("Backup completed: {$ok} ok, {$failed} failed.");
        return $failed > 0 ? self::FAILURE : self::SUCCESS;
    }

    /** Upload one backup zip to the offsite B2 disk if env is configured. */
    private function uploadOffsite(string $localPath, string $remoteKey): void
    {
        if (! env('B2_KEY') || ! env('B2_BUCKET')) return;
        try {
            $stream = fopen($localPath, 'rb');
            \Illuminate\Support\Facades\Storage::disk('b2')->put($remoteKey, $stream);
            if (is_resource($stream)) fclose($stream);
            $this->line("  ↑ offsite: {$remoteKey}");
        } catch (\Throwable $e) {
            $this->warn("  ✗ offsite upload failed: " . substr($e->getMessage(), 0, 120));
        }
    }

    private function cleanupOld(int $keep): void
    {
        $backupsDir = storage_path('app/backups');
        if (! is_dir($backupsDir)) return;

        $dates = collect(scandir($backupsDir))
            ->filter(fn ($d) => preg_match('/^\d{4}-\d{2}-\d{2}$/', $d))
            ->sortDesc()
            ->values();

        $toDelete = $dates->slice($keep);
        foreach ($toDelete as $dateDir) {
            $abs = "{$backupsDir}/{$dateDir}";
            if (is_dir($abs)) {
                array_map('unlink', glob("{$abs}/*"));
                @rmdir($abs);
                $this->line("Removed old backup: {$dateDir}");
            }
        }
    }
}
