<?php

namespace App\Console\Commands;

use App\Models\ThemeVersion;
use App\Services\AuditLogger;
use Illuminate\Console\Command;

/**
 * Retention sweep for `theme_versions`. Each save / import on the
 * Appearance page creates a new row; without pruning, the table + the
 * /admin/appearance Versions list grow indefinitely.
 *
 * Policy:
 *   - The currently-active version is NEVER deleted (deleting active
 *     would orphan the live site's CSS overlay; the admin would land on
 *     the seeded defaults until they apply another version).
 *   - Among inactive versions, keep the newest `--keep` by created_at
 *     DESC. Delete the rest. 50 is the documented default — large enough
 *     to cover a few months of iteration on a busy install, small enough
 *     to keep the Versions list usable.
 *   - Hard delete (the rows have no soft-delete column and no foreign
 *     keys; audit-log rows are independent and have their own retention
 *     via auditlog:purge).
 *
 * Scheduled weekly. Surfaced at /admin/maintenance for manual triggering
 * (Preview + Purge + tunable keep count).
 */
class PurgeThemeVersionsCommand extends Command
{
    protected $signature = 'themes:purge-versions
                            {--keep=50 : Inactive versions to retain (active is always kept)}
                            {--max-time=55 : Stop after N seconds — keeps the run under shared-hosting exec limits}
                            {--dry-run : Print what would be deleted without writing}';

    protected $description = 'Delete inactive theme_versions rows beyond the retention cap (active version always kept).';

    public function handle(): int
    {
        $keep = max(1, (int) $this->option('keep'));
        $maxTime = max(5, (int) $this->option('max-time'));
        $dryRun = (bool) $this->option('dry-run');

        @set_time_limit($maxTime + 5);
        $deadline = microtime(true) + $maxTime;

        // Collect ids to delete: every inactive row OUTSIDE the top-N
        // window (ordered newest-first). The active row is filtered out
        // upfront so it can never sneak into the keep-window calculation.
        $eligibleIds = ThemeVersion::query()
            ->where('is_active', false)
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->pluck('id')
            ->slice($keep) // skip the first $keep — those stay.
            ->values()
            ->all();

        $this->line(sprintf('Keep: %d most recent inactive versions', $keep));
        $this->line(sprintf('Eligible to delete: %d', count($eligibleIds)));

        if ($eligibleIds === []) {
            return self::SUCCESS;
        }

        if ($dryRun) {
            foreach (array_slice($eligibleIds, 0, 20) as $id) {
                $row = ThemeVersion::query()->find($id, ['id', 'name', 'created_at']);
                if ($row) {
                    $this->line(sprintf(
                        '[dry-run] would delete: #%d "%s" (created %s)',
                        $row->id,
                        (string) $row->name,
                        $row->created_at?->toDateString() ?? '?',
                    ));
                }
            }
            if (count($eligibleIds) > 20) {
                $this->line(sprintf('... and %d more', count($eligibleIds) - 20));
            }

            return self::SUCCESS;
        }

        $deleted = 0;
        $stoppedEarly = false;
        // Chunked delete so a large backlog can yield to the deadline cleanly.
        foreach (array_chunk($eligibleIds, 200) as $chunk) {
            if (microtime(true) >= $deadline) {
                $stoppedEarly = true;
                break;
            }
            $deleted += ThemeVersion::query()->whereIn('id', $chunk)->delete();
        }

        AuditLogger::record(
            'theme.purgeVersions',
            null,
            "Purged {$deleted} inactive theme version(s) (kept newest {$keep})",
            [
                'keep' => $keep,
                'deleted' => $deleted,
                'stopped_early' => $stoppedEarly,
            ],
        );

        $this->line(sprintf('Deleted: %d', $deleted));
        if ($stoppedEarly) {
            $this->warn('Stopped at deadline — re-run to continue.');
        }

        return self::SUCCESS;
    }
}
