<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Support\OperationCatalog;
use App\Support\OperationRunType;
use Illuminate\Console\Command;

class PurgeLegacyBaselineGapRuns extends Command
{
    protected $signature = 'tenantpilot:baselines:purge-legacy-gap-runs
        {--type=* : Limit cleanup to baseline.compare and/or baseline.capture runs (legacy aliases also accepted)}
        {--tenant=* : Limit cleanup to tenant ids or tenant external ids}
        {--workspace=* : Limit cleanup to workspace ids}
        {--limit=500 : Maximum candidate runs to inspect}
        {--force : Actually delete matched legacy runs}';

    protected $description = 'Purge development-only baseline compare/capture runs that still use legacy broad gap payloads.';

    public function handle(): int
    {
        if (! app()->environment(['local', 'testing'])) {
            $this->error('This cleanup command is limited to local and testing environments.');

            return self::FAILURE;
        }

        $types = $this->normalizedTypes();
        $workspaceIds = array_values(array_filter(
            array_map(
                static fn (mixed $workspaceId): int => is_numeric($workspaceId) ? (int) $workspaceId : 0,
                (array) $this->option('workspace'),
            ),
            static fn (int $workspaceId): bool => $workspaceId > 0,
        ));
        $tenantIds = $this->resolveTenantIds(array_values(array_filter((array) $this->option('tenant'))));
        $limit = max(1, (int) $this->option('limit'));
        $dryRun = ! (bool) $this->option('force');

        $query = OperationRun::query()
            ->whereIn('type', $types)
            ->orderBy('id')
            ->limit($limit);

        if ($workspaceIds !== []) {
            $query->whereIn('workspace_id', $workspaceIds);
        }

        if ($tenantIds !== []) {
            $query->whereIn('managed_environment_id', $tenantIds);
        }

        $candidates = $query->get();
        $matched = $candidates
            ->filter(static fn (OperationRun $run): bool => $run->hasLegacyBaselineGapPayload())
            ->values();

        if ($matched->isEmpty()) {
            $this->info('No legacy baseline gap runs matched the current filters.');

            return self::SUCCESS;
        }

        $this->table(
            ['Run', 'Type', 'ManagedEnvironment', 'Workspace', 'Legacy signal'],
            $matched
                ->map(fn (OperationRun $run): array => [
                    'Run' => (string) $run->getKey(),
                    'Type' => (string) $run->type,
                    'ManagedEnvironment' => $run->managed_environment_id !== null ? (string) $run->managed_environment_id : '—',
                    'Workspace' => $run->workspace_id !== null ? (string) $run->workspace_id : '—',
                    'Legacy signal' => $this->legacySignal($run),
                ])
                ->all(),
        );

        if ($dryRun) {
            $this->warn(sprintf(
                'Dry run: matched %d legacy baseline run(s). Re-run with --force to delete them.',
                $matched->count(),
            ));

            return self::SUCCESS;
        }

        OperationRun::query()
            ->whereKey($matched->modelKeys())
            ->delete();

        $this->info(sprintf('Deleted %d legacy baseline run(s).', $matched->count()));

        return self::SUCCESS;
    }

    /**
     * @return array<int, string>
     */
    private function normalizedTypes(): array
    {
        $requestedTypes = array_values(array_unique(array_filter(
            array_map(
                static fn (mixed $type): ?string => is_string($type) && trim($type) !== '' ? trim($type) : null,
                (array) $this->option('type'),
            ),
        )));

        $canonicalTypes = array_values(array_unique(array_filter(array_map(
            static fn (string $type): ?string => match ($type) {
                OperationRunType::BaselineCompare->value, 'baseline_compare' => OperationRunType::BaselineCompare->value,
                OperationRunType::BaselineCapture->value, 'baseline_capture' => OperationRunType::BaselineCapture->value,
                default => null,
            },
            $requestedTypes,
        ))));

        if ($canonicalTypes === []) {
            $canonicalTypes = [
                OperationRunType::BaselineCompare->value,
                OperationRunType::BaselineCapture->value,
            ];
        }

        return array_values(array_unique(array_merge(
            ...array_map(
                static fn (string $type): array => OperationCatalog::rawValuesForCanonical($type),
                $canonicalTypes,
            ),
        )));
    }

    /**
     * @param  array<int, string>  $tenantIdentifiers
     * @return array<int, int>
     */
    private function resolveTenantIds(array $tenantIdentifiers): array
    {
        if ($tenantIdentifiers === []) {
            return [];
        }

        $tenantIds = [];

        foreach ($tenantIdentifiers as $identifier) {
            $tenant = ManagedEnvironment::query()->forTenant($identifier)->first();

            if ($tenant instanceof ManagedEnvironment) {
                $tenantIds[] = (int) $tenant->getKey();
            }
        }

        return array_values(array_unique($tenantIds));
    }

    private function legacySignal(OperationRun $run): string
    {
        $byReason = $run->baselineGapEnvelope()['by_reason'] ?? null;
        $byReason = is_array($byReason) ? $byReason : [];

        if (array_key_exists('policy_not_found', $byReason)) {
            return 'legacy_reason_code';
        }

        return 'legacy_subject_shape';
    }
}
