<?php

declare(strict_types=1);

namespace App\Support\PortfolioTriage;

use App\Models\ManagedEnvironmentTriageReview;
use App\Support\BackupHealth\TenantBackupHealthAssessment;

final readonly class ManagedEnvironmentTriageReviewStateResolver
{
    public function __construct(
        private ManagedEnvironmentTriageReviewFingerprint $fingerprints,
    ) {}

    /**
     * @param  list<int>  $tenantIds
     * @param  array<int, TenantBackupHealthAssessment>  $backupHealthByTenant
     * @param  array<int, array<string, mixed>>  $recoveryEvidenceByTenant
     * @return array{
     *     rows: array<int, array{
     *         backup_health: array<string, mixed>,
     *         recovery_evidence: array<string, mixed>
     *     }>,
     *     summaries: array<string, array{
     *         concern_family: string,
     *         affected_total: int,
     *         reviewed_count: int,
     *         follow_up_needed_count: int,
     *         changed_since_review_count: int,
     *         not_reviewed_count: int
     *     }>
     * }
     */
    public function resolveMany(
        int $workspaceId,
        array $tenantIds,
        array $backupHealthByTenant = [],
        array $recoveryEvidenceByTenant = [],
    ): array {
        $tenantIds = array_values(array_unique(array_map(static fn (int|string $tenantId): int => (int) $tenantId, $tenantIds)));

        $emptySummary = [
            PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => $this->emptySummary(PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH),
            PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => $this->emptySummary(PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE),
        ];

        if ($workspaceId <= 0 || $tenantIds === []) {
            return [
                'rows' => [],
                'summaries' => $emptySummary,
            ];
        }

        $activeReviews = ManagedEnvironmentTriageReview::query()
            ->with('reviewer:id,name,email')
            ->forWorkspace($workspaceId)
            ->whereIn('managed_environment_id', $tenantIds)
            ->active()
            ->orderByDesc('reviewed_at')
            ->orderByDesc('id')
            ->get()
            ->groupBy([
                'managed_environment_id',
                'concern_family',
            ]);

        $rows = [];
        $summaries = $emptySummary;

        foreach ($tenantIds as $tenantId) {
            $rows[$tenantId] = [
                PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => $this->resolveFamily(
                    tenantId: $tenantId,
                    concernFamily: PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
                    currentConcern: $this->fingerprints->forBackupHealth($backupHealthByTenant[$tenantId] ?? null),
                    activeReview: $activeReviews[$tenantId][PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH][0] ?? null,
                ),
                PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => $this->resolveFamily(
                    tenantId: $tenantId,
                    concernFamily: PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
                    currentConcern: $this->fingerprints->forRecoveryEvidence($recoveryEvidenceByTenant[$tenantId] ?? null),
                    activeReview: $activeReviews[$tenantId][PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE][0] ?? null,
                ),
            ];

            foreach ($rows[$tenantId] as $family => $row) {
                if (($row['current_concern_present'] ?? false) !== true) {
                    continue;
                }

                $summaries[$family]['affected_total']++;

                match ($row['derived_state']) {
                    ManagedEnvironmentTriageReview::STATE_REVIEWED => $summaries[$family]['reviewed_count']++,
                    ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $summaries[$family]['follow_up_needed_count']++,
                    ManagedEnvironmentTriageReview::DERIVED_STATE_CHANGED_SINCE_REVIEW => $summaries[$family]['changed_since_review_count']++,
                    default => $summaries[$family]['not_reviewed_count']++,
                };
            }
        }

        return [
            'rows' => $rows,
            'summaries' => $summaries,
        ];
    }

    /**
     * @param  array{
     *     concern_family: string,
     *     concern_state: string,
     *     fingerprint: string,
     *     snapshot: array<string, mixed>
     * }|null  $currentConcern
     * @return array<string, mixed>
     */
    private function resolveFamily(
        int $tenantId,
        string $concernFamily,
        ?array $currentConcern,
        ?ManagedEnvironmentTriageReview $activeReview,
    ): array {
        $reviewerName = null;

        if ($activeReview?->reviewer !== null) {
            $reviewerName = filled($activeReview->reviewer->name)
                ? (string) $activeReview->reviewer->name
                : (filled($activeReview->reviewer->email) ? (string) $activeReview->reviewer->email : null);
        }

        if ($currentConcern === null) {
            return [
                'managed_environment_id' => $tenantId,
                'concern_family' => $concernFamily,
                'current_concern_present' => false,
                'current_state' => null,
                'current_fingerprint' => null,
                'review_fingerprint' => $activeReview?->review_fingerprint,
                'derived_state' => null,
                'reviewed_at' => $activeReview?->reviewed_at,
                'reviewed_by_user_id' => $activeReview?->reviewed_by_user_id,
                'reviewed_by_user_name' => $reviewerName,
                'current_snapshot' => null,
                'review_snapshot' => is_array($activeReview?->review_snapshot) ? $activeReview->review_snapshot : null,
            ];
        }

        $derivedState = match (true) {
            ! $activeReview instanceof ManagedEnvironmentTriageReview => ManagedEnvironmentTriageReview::DERIVED_STATE_NOT_REVIEWED,
            $activeReview->review_fingerprint !== $currentConcern['fingerprint'] => ManagedEnvironmentTriageReview::DERIVED_STATE_CHANGED_SINCE_REVIEW,
            default => (string) $activeReview->current_state,
        };

        return [
            'managed_environment_id' => $tenantId,
            'concern_family' => $concernFamily,
            'current_concern_present' => true,
            'current_state' => $currentConcern['concern_state'],
            'current_fingerprint' => $currentConcern['fingerprint'],
            'review_fingerprint' => $activeReview?->review_fingerprint,
            'derived_state' => $derivedState,
            'reviewed_at' => $activeReview?->reviewed_at,
            'reviewed_by_user_id' => $activeReview?->reviewed_by_user_id,
            'reviewed_by_user_name' => $reviewerName,
            'current_snapshot' => $currentConcern['snapshot'],
            'review_snapshot' => is_array($activeReview?->review_snapshot) ? $activeReview->review_snapshot : $currentConcern['snapshot'],
        ];
    }

    /**
     * @return array{
     *     concern_family: string,
     *     affected_total: int,
     *     reviewed_count: int,
     *     follow_up_needed_count: int,
     *     changed_since_review_count: int,
     *     not_reviewed_count: int
     * }
     */
    private function emptySummary(string $concernFamily): array
    {
        return [
            'concern_family' => $concernFamily,
            'affected_total' => 0,
            'reviewed_count' => 0,
            'follow_up_needed_count' => 0,
            'changed_since_review_count' => 0,
            'not_reviewed_count' => 0,
        ];
    }
}
