<?php

declare(strict_types=1);

namespace App\Services\Evidence\Sources;

use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Services\Evidence\Contracts\EvidenceSourceProvider;
use App\Services\Findings\FindingRiskGovernanceResolver;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Artifacts\ArtifactSourceResolver;

final class FindingsSummarySource implements EvidenceSourceProvider
{
    public function __construct(
        private readonly FindingRiskGovernanceResolver $governanceResolver,
        private readonly FindingOutcomeSemantics $findingOutcomeSemantics,
        private readonly ArtifactSourceResolver $artifactSourceResolver,
    ) {}

    public function key(): string
    {
        return 'findings_summary';
    }

    public function collect(ManagedEnvironment $tenant): array
    {
        $findings = Finding::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->with('findingException.currentDecision')
            ->orderByDesc('updated_at')
            ->get();

        $latest = $findings->max('updated_at') ?? $findings->max('created_at');
        $entries = $findings->map(function (Finding $finding): array {
            $governanceState = $this->governanceResolver->resolveFindingState($finding, $finding->findingException);
            $governanceWarning = $this->governanceResolver->resolveWarningMessage($finding, $finding->findingException);
            $outcome = $this->findingOutcomeSemantics->describe($finding);
            $descriptor = $this->artifactSourceResolver->forFinding($finding);
            $providerDetail = $this->artifactSourceResolver->providerDetailForFinding($finding);
            $canonicalControlResolution = $this->artifactSourceResolver->canonicalControlResolutionForFinding($finding);

            return [
                'id' => (int) $finding->getKey(),
                'source_descriptor' => $descriptor->toArray(),
                'provider_detail' => $providerDetail->toArray(),
                'control_key' => $descriptor->controlKey,
                'finding_type' => (string) $finding->finding_type,
                'severity' => (string) $finding->severity,
                'status' => (string) $finding->status,
                'title' => $finding->resolvedSubjectDisplayName(),
                'description' => null,
                'created_at' => $finding->created_at?->toIso8601String(),
                'updated_at' => $finding->updated_at?->toIso8601String(),
                'verification_state' => $outcome['verification_state'],
                'report_bucket' => $outcome['report_bucket'],
                'terminal_outcome_key' => $outcome['terminal_outcome_key'],
                'terminal_outcome_label' => $outcome['label'],
                'terminal_outcome' => $outcome['terminal_outcome_key'] !== null ? [
                    'key' => $outcome['terminal_outcome_key'],
                    'label' => $outcome['label'],
                    'verification_state' => $outcome['verification_state'],
                    'report_bucket' => $outcome['report_bucket'],
                    'governance_state' => $governanceState,
                ] : null,
                'canonical_control_resolution' => $canonicalControlResolution,
                'governance_state' => $governanceState,
                'governance_warning' => $governanceWarning,
            ];
        });
        $outcomeCounts = array_fill_keys($this->findingOutcomeSemantics->orderedOutcomeKeys(), 0);
        $reportBucketCounts = [
            'remediation_pending_verification' => 0,
            'remediation_verified' => 0,
            'administrative_closure' => 0,
            'accepted_risk' => 0,
        ];

        foreach ($entries as $entry) {
            $terminalOutcomeKey = $entry['terminal_outcome_key'] ?? null;
            $reportBucket = $entry['report_bucket'] ?? null;

            if (is_string($terminalOutcomeKey) && array_key_exists($terminalOutcomeKey, $outcomeCounts)) {
                $outcomeCounts[$terminalOutcomeKey]++;
            }

            if (is_string($reportBucket) && array_key_exists($reportBucket, $reportBucketCounts)) {
                $reportBucketCounts[$reportBucket]++;
            }
        }
        $canonicalControls = $entries
            ->map(static fn (array $entry): mixed => data_get($entry, 'canonical_control_resolution.control'))
            ->filter(static fn (mixed $control): bool => is_array($control) && filled($control['control_key'] ?? null))
            ->unique(static fn (array $control): string => (string) $control['control_key'])
            ->values()
            ->all();

        $riskAcceptedEntries = $entries->filter(
            static fn (array $entry): bool => ($entry['status'] ?? null) === Finding::STATUS_RISK_ACCEPTED,
        );
        $warningStates = [
            'expired_exception',
            'revoked_exception',
            'rejected_exception',
            'risk_accepted_without_valid_exception',
        ];

        $summary = [
            'count' => $findings->count(),
            'open_count' => $findings->filter(fn (Finding $finding): bool => $finding->hasOpenStatus())->count(),
            'severity_counts' => [
                'critical' => $findings->where('severity', Finding::SEVERITY_CRITICAL)->count(),
                'high' => $findings->where('severity', Finding::SEVERITY_HIGH)->count(),
                'medium' => $findings->where('severity', Finding::SEVERITY_MEDIUM)->count(),
                'low' => $findings->where('severity', Finding::SEVERITY_LOW)->count(),
            ],
            'risk_acceptance' => [
                'status_marked_count' => $riskAcceptedEntries->count(),
                'valid_governed_count' => $riskAcceptedEntries->filter(
                    static fn (array $entry): bool => in_array($entry['governance_state'] ?? null, ['valid_exception', 'expiring_exception'], true),
                )->count(),
                'warning_count' => $riskAcceptedEntries->filter(
                    static fn (array $entry): bool => in_array($entry['governance_state'] ?? null, $warningStates, true),
                )->count(),
                'expired_count' => $riskAcceptedEntries->where('governance_state', 'expired_exception')->count(),
                'revoked_count' => $riskAcceptedEntries->where('governance_state', 'revoked_exception')->count(),
                'missing_exception_count' => $riskAcceptedEntries->where('governance_state', 'risk_accepted_without_valid_exception')->count(),
            ],
            'outcome_counts' => $outcomeCounts,
            'report_bucket_counts' => $reportBucketCounts,
            'canonical_controls' => $canonicalControls,
            'entries' => $entries->all(),
        ];

        return [
            'dimension_key' => $this->key(),
            'state' => $findings->isEmpty() ? EvidenceCompletenessState::Missing->value : EvidenceCompletenessState::Complete->value,
            'required' => true,
            'source_kind' => 'model_summary',
            'source_record_type' => 'finding',
            'source_record_id' => null,
            'source_fingerprint' => $findings->max('fingerprint'),
            'measured_at' => $latest,
            'freshness_at' => $latest,
            'summary_payload' => $summary,
            'fingerprint_payload' => $summary + ['latest' => $latest?->format(DATE_ATOM)],
            'sort_order' => 10,
        ];
    }
}
