<?php

declare(strict_types=1);

namespace App\Support\Baselines\Compare;

use InvalidArgumentException;

final class CompareStrategySelection
{
    /**
     * @param  list<array<string, mixed>>  $matchedScopeEntries
     * @param  list<array<string, mixed>>  $rejectedScopeEntries
     * @param  array<string, mixed>  $diagnostics
     */
    public function __construct(
        public readonly StrategySelectionState $selectionState,
        public readonly ?CompareStrategyKey $strategyKey,
        public readonly array $matchedScopeEntries,
        public readonly array $rejectedScopeEntries,
        public readonly string $operatorReason,
        public readonly array $diagnostics = [],
    ) {
        if ($this->selectionState === StrategySelectionState::Supported && ! $this->strategyKey instanceof CompareStrategyKey) {
            throw new InvalidArgumentException('Supported compare strategy selections require a strategy key.');
        }

        if (trim($this->operatorReason) === '') {
            throw new InvalidArgumentException('Compare strategy selections require an operator-safe reason.');
        }
    }

    /**
     * @param  list<array<string, mixed>>  $matchedScopeEntries
     * @param  array<string, mixed>  $diagnostics
     */
    public static function supported(
        CompareStrategyKey|string $strategyKey,
        array $matchedScopeEntries,
        array $diagnostics = [],
        string $operatorReason = 'Compare strategy resolved successfully.',
    ): self {
        return new self(
            selectionState: StrategySelectionState::Supported,
            strategyKey: CompareStrategyKey::from($strategyKey),
            matchedScopeEntries: $matchedScopeEntries,
            rejectedScopeEntries: [],
            operatorReason: $operatorReason,
            diagnostics: $diagnostics,
        );
    }

    /**
     * @param  list<array<string, mixed>>  $matchedScopeEntries
     * @param  list<array<string, mixed>>  $rejectedScopeEntries
     * @param  array<string, mixed>  $diagnostics
     */
    public static function unsupported(
        array $matchedScopeEntries,
        array $rejectedScopeEntries,
        array $diagnostics = [],
        string $operatorReason = 'No compare strategy supports the selected governed subjects.',
    ): self {
        return new self(
            selectionState: StrategySelectionState::Unsupported,
            strategyKey: null,
            matchedScopeEntries: $matchedScopeEntries,
            rejectedScopeEntries: $rejectedScopeEntries,
            operatorReason: $operatorReason,
            diagnostics: $diagnostics,
        );
    }

    /**
     * @param  list<array<string, mixed>>  $matchedScopeEntries
     * @param  array<string, mixed>  $diagnostics
     */
    public static function mixed(
        array $matchedScopeEntries,
        array $diagnostics = [],
        string $operatorReason = 'The selected governed subjects span multiple compare strategy families.',
    ): self {
        return new self(
            selectionState: StrategySelectionState::Mixed,
            strategyKey: null,
            matchedScopeEntries: $matchedScopeEntries,
            rejectedScopeEntries: [],
            operatorReason: $operatorReason,
            diagnostics: $diagnostics,
        );
    }

    public function isSupported(): bool
    {
        return $this->selectionState === StrategySelectionState::Supported;
    }

    public function isUnsupported(): bool
    {
        return $this->selectionState === StrategySelectionState::Unsupported;
    }

    public function isMixed(): bool
    {
        return $this->selectionState === StrategySelectionState::Mixed;
    }

    /**
     * @return array{
     *   selection_state: string,
     *   strategy_key: ?string,
     *   matched_scope_entries: list<array<string, mixed>>,
     *   rejected_scope_entries: list<array<string, mixed>>,
     *   operator_reason: string,
     *   diagnostics: array<string, mixed>
     * }
     */
    public function toArray(): array
    {
        return [
            'selection_state' => $this->selectionState->value,
            'strategy_key' => $this->strategyKey?->value,
            'matched_scope_entries' => $this->matchedScopeEntries,
            'rejected_scope_entries' => $this->rejectedScopeEntries,
            'operator_reason' => $this->operatorReason,
            'diagnostics' => $this->diagnostics,
        ];
    }
}