<?php

declare(strict_types=1);

namespace App\Support\PortfolioTriage;

use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\Tenants\TenantRecoveryTriagePresentation;
use JsonException;

final class ManagedEnvironmentTriageReviewFingerprint
{
    /**
     * @param  array<string, mixed>|null  $recoveryEvidence
     * @return array{
     *     concern_family: string,
     *     concern_state: string,
     *     fingerprint: string,
     *     snapshot: array{
     *         concernFamily: string,
     *         concernState: string,
     *         reasonCode: ?string,
     *         severityKey: ?string,
     *         supportingKey: ?string
     *     }
     * }|null
     */
    public function forConcernFamily(
        string $concernFamily,
        ?TenantBackupHealthAssessment $backupHealth,
        ?array $recoveryEvidence,
    ): ?array {
        return match ($concernFamily) {
            PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => $this->forBackupHealth($backupHealth),
            PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => $this->forRecoveryEvidence($recoveryEvidence),
            default => null,
        };
    }

    /**
     * @return array{
     *     concern_family: string,
     *     concern_state: string,
     *     fingerprint: string,
     *     snapshot: array{
     *         concernFamily: string,
     *         concernState: string,
     *         reasonCode: ?string,
     *         severityKey: ?string,
     *         supportingKey: ?string
     *     }
     * }|null
     */
    public function forBackupHealth(?TenantBackupHealthAssessment $assessment): ?array
    {
        if (! $assessment instanceof TenantBackupHealthAssessment) {
            return null;
        }

        if (! in_array($assessment->posture, [
            TenantBackupHealthAssessment::POSTURE_ABSENT,
            TenantBackupHealthAssessment::POSTURE_STALE,
            TenantBackupHealthAssessment::POSTURE_DEGRADED,
        ], true)) {
            return null;
        }

        $snapshot = [
            'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
            'concernState' => $assessment->posture,
            'reasonCode' => $assessment->primaryReason,
            'severityKey' => $assessment->primaryReason,
            'supportingKey' => $assessment->primaryReason,
        ];

        return [
            'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
            'concern_state' => $assessment->posture,
            'fingerprint' => $this->hash($snapshot),
            'snapshot' => $snapshot,
        ];
    }

    /**
     * @param  array<string, mixed>|null  $recoveryEvidence
     * @return array{
     *     concern_family: string,
     *     concern_state: string,
     *     fingerprint: string,
     *     snapshot: array{
     *         concernFamily: string,
     *         concernState: string,
     *         reasonCode: ?string,
     *         severityKey: ?string,
     *         supportingKey: ?string
     *     }
     * }|null
     */
    public function forRecoveryEvidence(?array $recoveryEvidence): ?array
    {
        $state = is_array($recoveryEvidence)
            ? TenantRecoveryTriagePresentation::recoveryEvidenceState($recoveryEvidence)
            : null;

        if (! in_array($state, [
            TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED,
            TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_UNVALIDATED,
        ], true)) {
            return null;
        }

        $reason = is_string($recoveryEvidence['reason'] ?? null)
            ? $recoveryEvidence['reason']
            : null;
        $supportingKey = is_string($recoveryEvidence['latest_relevant_attention_state'] ?? null)
            ? $recoveryEvidence['latest_relevant_attention_state']
            : $reason;

        $snapshot = [
            'concernFamily' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
            'concernState' => $state,
            'reasonCode' => $reason,
            'severityKey' => $reason,
            'supportingKey' => $supportingKey,
        ];

        return [
            'concern_family' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
            'concern_state' => $state,
            'fingerprint' => $this->hash($snapshot),
            'snapshot' => $snapshot,
        ];
    }

    /**
     * @param  array{
     *     concernFamily: string,
     *     concernState: string,
     *     reasonCode: ?string,
     *     severityKey: ?string,
     *     supportingKey: ?string
     * }  $snapshot
     */
    private function hash(array $snapshot): string
    {
        try {
            return hash('sha256', json_encode($snapshot, JSON_THROW_ON_ERROR));
        } catch (JsonException) {
            return hash('sha256', implode(':', array_map(
                static fn (mixed $value): string => is_scalar($value) || $value === null
                    ? (string) $value
                    : '',
                $snapshot,
            )));
        }
    }
}
