<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\ReportJob;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

/**
 * Cron-команда удаления expired report-files.
 *
 * Идёт по `report_jobs` где `status='done' AND expires_at < NOW() AND
 * file_path IS NOT NULL`. Для каждой строки: удаляет файл из disk('local')
 * + UPDATE file_path=NULL.
 *
 * CTO-10: статус 'done' СОХРАНЯЕТСЯ; только file_path обнуляется. Это
 * позволяет UI показать «истёк» при отсутствии file_path. Сама запись
 * остаётся в истории.
 *
 * Failed jobs игнорируем — у них file_path обычно NULL, нечего чистить;
 * пользователь сам удалит через DELETE /api/reports/jobs/{id}.
 *
 * Запускается ежесуточно через Windows Task Scheduler / cron.
 *
 * --dry-run печатает плановые удаления без реальных операций.
 */
class ReportsCleanupExpired extends Command
{
    /** @var string */
    protected $signature = 'reports:cleanup-expired
        {--dry-run : Не удалять, только напечатать список плановых удалений}
        {--limit=1000 : Максимум jobs за один запуск}';

    /** @var string */
    protected $description = 'Удаление expired report-files (CTO-10): file_path=NULL, status="done" сохраняется';

    public function handle(): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $limit = (int) $this->option('limit');

        // Cross-tenant gather via BYPASSRLS connection — crm_app_user on prod cannot
        // evaluate current_setting('app.current_tenant_id') without a GUC set.
        $rows = DB::connection('pgsql_supplier')
            ->table('report_jobs')
            ->select(['id', 'tenant_id', 'file_path', 'expires_at'])
            ->where('status', ReportJob::STATUS_DONE)
            ->whereNotNull('file_path')
            ->where('expires_at', '<', Carbon::now())
            ->orderBy('expires_at')
            ->limit($limit)
            ->get();

        if ($rows->isEmpty()) {
            $this->info('Нет expired report-files для удаления.');

            return self::SUCCESS;
        }

        $count = 0;
        foreach ($rows as $row) {
            $this->line(sprintf(
                '[%s] tenant=%d job=%d path=%s expired_at=%s',
                $dryRun ? 'DRY' : 'DEL',
                $row->tenant_id,
                $row->id,
                $row->file_path,
                $row->expires_at ?? '?',
            ));

            if (! $dryRun) {
                Storage::disk('local')->delete($row->file_path);

                // Both writes go through pgsql_supplier (BYPASSRLS) — this is a
                // SaaS-admin cron, not a per-user action, so no tenant GUC is
                // required.  Same pattern as IncidentsWatchFailures, Reset*.
                DB::connection('pgsql_supplier')->table('pd_processing_log')->insert([
                    'tenant_id' => $row->tenant_id,
                    'subject_type' => 'lead',
                    'subject_id' => null,
                    'action' => 'deleted',
                    'purpose' => 'report_cleanup_expired_'.$row->id,
                    'actor_tenant_user_id' => null,
                    'actor_admin_user_id' => null,
                    'ip_address' => null,
                    'created_at' => now(),
                ]);

                DB::connection('pgsql_supplier')
                    ->table('report_jobs')
                    ->where('id', $row->id)
                    ->update(['file_path' => null]);
            }
            $count++;
        }

        $this->info(sprintf(
            '%s %d файлов отчётов.',
            $dryRun ? 'Будет удалено:' : 'Удалено:',
            $count
        ));

        return self::SUCCESS;
    }
}
