<?php

declare(strict_types=1);

namespace App\Services\PortfolioTriage;

use App\Models\Tenant;
use App\Models\TenantTriageReview;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\PortfolioTriage\TenantTriageReviewFingerprint;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;

final readonly class TenantTriageReviewService
{
    public function __construct(
        private TenantTriageReviewFingerprint $fingerprints,
        private WorkspaceAuditLogger $auditLogger,
    ) {}

    /**
     * @param  array<string, mixed>|null  $recoveryEvidence
     */
    public function markReviewed(
        Tenant $tenant,
        string $concernFamily,
        ?TenantBackupHealthAssessment $backupHealth = null,
        ?array $recoveryEvidence = null,
        ?User $actor = null,
    ): TenantTriageReview {
        return $this->store(
            tenant: $tenant,
            concernFamily: $concernFamily,
            manualState: TenantTriageReview::STATE_REVIEWED,
            backupHealth: $backupHealth,
            recoveryEvidence: $recoveryEvidence,
            actor: $actor,
        );
    }

    /**
     * @param  array<string, mixed>|null  $recoveryEvidence
     */
    public function markFollowUpNeeded(
        Tenant $tenant,
        string $concernFamily,
        ?TenantBackupHealthAssessment $backupHealth = null,
        ?array $recoveryEvidence = null,
        ?User $actor = null,
    ): TenantTriageReview {
        return $this->store(
            tenant: $tenant,
            concernFamily: $concernFamily,
            manualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
            backupHealth: $backupHealth,
            recoveryEvidence: $recoveryEvidence,
            actor: $actor,
        );
    }

    /**
     * @param  array<string, mixed>|null  $recoveryEvidence
     */
    private function store(
        Tenant $tenant,
        string $concernFamily,
        string $manualState,
        ?TenantBackupHealthAssessment $backupHealth,
        ?array $recoveryEvidence,
        ?User $actor,
    ): TenantTriageReview {
        if (! in_array($manualState, TenantTriageReview::MANUAL_STATES, true)) {
            throw new InvalidArgumentException('Unsupported triage review state.');
        }

        if (! is_numeric($tenant->workspace_id) || (int) $tenant->workspace_id <= 0) {
            throw new InvalidArgumentException('Tenant must belong to a workspace.');
        }

        $currentConcern = $this->fingerprints->forConcernFamily($concernFamily, $backupHealth, $recoveryEvidence);

        if ($currentConcern === null) {
            throw new InvalidArgumentException('No current triage concern is available for review.');
        }

        $workspaceId = (int) $tenant->workspace_id;
        $now = now();

        /** @var TenantTriageReview $review */
        $review = DB::transaction(function () use (
            $tenant,
            $workspaceId,
            $manualState,
            $currentConcern,
            $actor,
            $now,
        ): TenantTriageReview {
            TenantTriageReview::query()
                ->forWorkspace($workspaceId)
                ->forTenant((int) $tenant->getKey())
                ->where('concern_family', $currentConcern['concern_family'])
                ->active()
                ->update([
                    'resolved_at' => $now,
                    'updated_at' => $now,
                ]);

            return TenantTriageReview::query()->create([
                'workspace_id' => $workspaceId,
                'tenant_id' => (int) $tenant->getKey(),
                'concern_family' => $currentConcern['concern_family'],
                'current_state' => $manualState,
                'reviewed_at' => $now,
                'reviewed_by_user_id' => $actor?->getKey(),
                'review_fingerprint' => $currentConcern['fingerprint'],
                'review_snapshot' => $currentConcern['snapshot'],
                'last_seen_matching_at' => $now,
                'resolved_at' => null,
            ]);
        });

        $review->loadMissing('reviewer');

        $this->auditLogger->log(
            workspace: $tenant->workspace,
            action: $manualState === TenantTriageReview::STATE_REVIEWED
                ? AuditActionId::TenantTriageReviewMarkedReviewed
                : AuditActionId::TenantTriageReviewMarkedFollowUpNeeded,
            context: [
                'metadata' => [
                    'concern_family' => $currentConcern['concern_family'],
                    'concern_state' => $currentConcern['concern_state'],
                    'reason_code' => $currentConcern['snapshot']['reasonCode'] ?? null,
                    'review_state' => $manualState,
                ],
            ],
            actor: $actor,
            resourceType: 'tenant_triage_review',
            resourceId: (string) $review->getKey(),
            targetLabel: $tenant->name,
            tenant: $tenant,
            summary: $this->summaryFor($currentConcern['concern_family'], $manualState),
        );

        return $review;
    }

    private function summaryFor(string $concernFamily, string $manualState): string
    {
        $family = match ($concernFamily) {
            'backup_health' => 'Backup health',
            'recovery_evidence' => 'Recovery evidence',
            default => 'Portfolio concern',
        };

        $state = $manualState === TenantTriageReview::STATE_REVIEWED
            ? 'reviewed'
            : 'follow-up needed';

        return sprintf('%s marked %s', $family, $state);
    }
}
