<?php

namespace App\Console\Commands;

use App\Models\AuditLog;
use App\Models\BackupItem;
use App\Models\BackupSchedule;
use App\Models\BackupSet;
use App\Models\OperationRun;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\RestoreRun;
use App\Models\ManagedEnvironment;
use App\Support\OperationRunType;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use RuntimeException;

class TenantpilotPurgeNonPersistentData extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'tenantpilot:purge-nonpersistent
                            {tenant? : ManagedEnvironment id / managed_environment_id / external_id (defaults to current tenant)}
                            {--all : Purge for all tenants}
                            {--force : Actually delete rows}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Permanently delete non-persistent tenant data like policies, backups, and runs while preserving durable audit logs.';

    /**
     * Execute the console command.
     */
    public function handle(): int
    {
        $tenants = $this->resolveTenants();

        if ($tenants->isEmpty()) {
            $this->error('No tenants selected. Provide {tenant} or use --all.');

            return self::FAILURE;
        }

        $isDryRun = ! (bool) $this->option('force');

        if ($isDryRun) {
            $this->warn('Dry run: no rows will be deleted. Re-run with --force to apply.');
        } else {
            $this->warn('This will PERMANENTLY delete non-persistent tenant data.');

            if ($this->input->isInteractive() && ! $this->confirm('Proceed?', false)) {
                $this->info('Aborted.');

                return self::SUCCESS;
            }
        }

        foreach ($tenants as $tenant) {
            $counts = $this->countsForTenant($tenant);

            $this->line('');
            $this->info("ManagedEnvironment: {$tenant->id} ({$tenant->name})");
            $this->table(
                ['Table', 'Rows'],
                collect($counts)
                    ->map(fn (int $count, string $table) => [$table, $count])
                    ->values()
                    ->all(),
            );

            if ($isDryRun) {
                continue;
            }

            DB::transaction(function () use ($tenant): void {
                BackupSchedule::query()
                    ->where('managed_environment_id', $tenant->id)
                    ->delete();

                OperationRun::query()
                    ->where('managed_environment_id', $tenant->id)
                    ->delete();

                RestoreRun::withTrashed()
                    ->where('managed_environment_id', $tenant->id)
                    ->forceDelete();

                BackupItem::withTrashed()
                    ->where('managed_environment_id', $tenant->id)
                    ->forceDelete();

                BackupSet::withTrashed()
                    ->where('managed_environment_id', $tenant->id)
                    ->forceDelete();

                PolicyVersion::withTrashed()
                    ->where('managed_environment_id', $tenant->id)
                    ->forceDelete();

                Policy::query()
                    ->where('managed_environment_id', $tenant->id)
                    ->delete();
            });

            $this->recordPurgeOperationRun($tenant, $counts);

            $this->info('Purged.');
        }

        return self::SUCCESS;
    }

    private function resolveTenants()
    {
        if ((bool) $this->option('all')) {
            return ManagedEnvironment::query()->get();
        }

        $tenantArg = $this->argument('tenant');

        if ($tenantArg !== null && $tenantArg !== '') {
            $tenant = ManagedEnvironment::query()->forTenant($tenantArg)->first();

            return $tenant ? collect([$tenant]) : collect();
        }

        try {
            return collect([ManagedEnvironment::currentOrFail()]);
        } catch (RuntimeException) {
            return collect();
        }
    }

    /**
     * @return array<string,int>
     */
    private function countsForTenant(ManagedEnvironment $tenant): array
    {
        return [
            'backup_schedules' => BackupSchedule::query()->where('managed_environment_id', $tenant->id)->count(),
            'operation_runs' => OperationRun::query()->where('managed_environment_id', $tenant->id)->count(),
            'audit_logs_retained' => AuditLog::query()->where('managed_environment_id', $tenant->id)->count(),
            'restore_runs' => RestoreRun::withTrashed()->where('managed_environment_id', $tenant->id)->count(),
            'backup_items' => BackupItem::withTrashed()->where('managed_environment_id', $tenant->id)->count(),
            'backup_sets' => BackupSet::withTrashed()->where('managed_environment_id', $tenant->id)->count(),
            'policy_versions' => PolicyVersion::withTrashed()->where('managed_environment_id', $tenant->id)->count(),
            'policies' => Policy::query()->where('managed_environment_id', $tenant->id)->count(),
        ];
    }

    /**
     * @param  array<string, int>  $counts
     */
    private function recordPurgeOperationRun(ManagedEnvironment $tenant, array $counts): void
    {
        $deletedRows = Arr::except($counts, ['audit_logs_retained']);

        OperationRun::query()->create([
            'workspace_id' => (int) $tenant->workspace_id,
            'managed_environment_id' => (int) $tenant->id,
            'user_id' => null,
            'initiator_name' => 'System',
            'type' => OperationRunType::BackupSchedulePurge->value,
            'status' => 'completed',
            'outcome' => 'succeeded',
            'run_identity_hash' => hash('sha256', implode(':', [
                (string) $tenant->id,
                OperationRunType::BackupSchedulePurge->value,
                now()->toISOString(),
                Str::uuid()->toString(),
            ])),
            'summary_counts' => [
                'total' => array_sum($deletedRows),
                'processed' => array_sum($deletedRows),
                'succeeded' => array_sum($deletedRows),
                'failed' => 0,
            ],
            'failure_summary' => [],
            'context' => [
                'source' => 'tenantpilot:purge-nonpersistent',
                'deleted_rows' => $deletedRows,
                'audit_logs_retained' => $counts['audit_logs_retained'] ?? 0,
            ],
            'started_at' => now(),
            'completed_at' => now(),
        ]);
    }
}
