<?php

declare(strict_types=1);

namespace App\Support\Baselines\Compare;

use InvalidArgumentException;

final class CompareSubjectResult
{
    /**
     * @param  array<string, mixed>  $diagnostics
     */
    public function __construct(
        public readonly CompareSubjectIdentity $subjectIdentity,
        public readonly CompareSubjectProjection $projection,
        public readonly string $baselineAvailability,
        public readonly string $currentStateAvailability,
        public readonly CompareState $compareState,
        public readonly string $trustLevel,
        public readonly string $evidenceQuality,
        public readonly ?string $severityRecommendation = null,
        public readonly ?CompareFindingCandidate $findingCandidate = null,
        public readonly array $diagnostics = [],
    ) {
        if (trim($this->baselineAvailability) === '' || trim($this->currentStateAvailability) === '' || trim($this->trustLevel) === '' || trim($this->evidenceQuality) === '') {
            throw new InvalidArgumentException('Compare subject results require non-empty availability, trust level, and evidence quality values.');
        }

        if ($this->compareState === CompareState::Drift && ! $this->findingCandidate instanceof CompareFindingCandidate) {
            throw new InvalidArgumentException('Drift compare subject results require a finding candidate.');
        }

        if ($this->compareState !== CompareState::Drift && $this->findingCandidate instanceof CompareFindingCandidate) {
            throw new InvalidArgumentException('Only drift compare subject results may carry a finding candidate.');
        }
    }

    public function hasFindingCandidate(): bool
    {
        return $this->findingCandidate instanceof CompareFindingCandidate;
    }

    public function isGapState(): bool
    {
        return in_array($this->compareState, [
            CompareState::Unsupported,
            CompareState::Incomplete,
            CompareState::Ambiguous,
            CompareState::Failed,
        ], true);
    }

    public function gapReasonCode(): ?string
    {
        $reasonCode = $this->diagnostics['reason_code'] ?? null;

        return is_string($reasonCode) && trim($reasonCode) !== '' ? trim($reasonCode) : null;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function gapRecord(): ?array
    {
        $gapRecord = $this->diagnostics['gap_record'] ?? null;

        return is_array($gapRecord) ? $gapRecord : null;
    }

    /**
     * @return array{
     *   subject_identity: array{
     *     domain_key: string,
     *     subject_class: string,
     *     subject_type_key: string,
     *     external_subject_id: ?string,
     *     subject_key: string
     *   },
     *   projection: array{
     *     platform_subject_class: string,
     *     domain_key: string,
     *     subject_type_key: string,
     *     operator_label: string,
     *     summary_kind: ?string,
     *     additional_labels: array<string, string>
     *   },
     *   baseline_availability: string,
     *   current_state_availability: string,
     *   compare_state: string,
     *   trust_level: string,
     *   evidence_quality: string,
     *   severity_recommendation: ?string,
     *   finding_candidate: ?array{
     *     change_type: string,
     *     severity: string,
     *     fingerprint_basis: array<string, mixed>,
     *     evidence_payload: array<string, mixed>,
     *     auto_close_eligible: bool
     *   },
     *   diagnostics: array<string, mixed>
     * }
     */
    public function toArray(): array
    {
        return [
            'subject_identity' => $this->subjectIdentity->toArray(),
            'projection' => $this->projection->toArray(),
            'baseline_availability' => $this->baselineAvailability,
            'current_state_availability' => $this->currentStateAvailability,
            'compare_state' => $this->compareState->value,
            'trust_level' => $this->trustLevel,
            'evidence_quality' => $this->evidenceQuality,
            'severity_recommendation' => $this->severityRecommendation,
            'finding_candidate' => $this->findingCandidate?->toArray(),
            'diagnostics' => $this->diagnostics,
        ];
    }
}