<?php

namespace App\Services\Providers;

use App\Models\ProviderConnection;
use App\Services\Providers\Contracts\HealthResult;
use App\Support\Providers\ProviderConsentStatus;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\TargetScope\ProviderConnectionSurfaceSummary;
use App\Support\Providers\ProviderVerificationStatus;

final class ProviderConnectionStateProjector
{
    public function surfaceSummary(ProviderConnection $connection): ProviderConnectionSurfaceSummary
    {
        return ProviderConnectionSurfaceSummary::forConnection($connection);
    }

    /**
     * @return array{
     *   consent_status: ProviderConsentStatus,
     *   verification_status: ProviderVerificationStatus,
     *   last_error_reason_code: ?string,
     *   last_error_message: ?string,
     *   consent_error_code: ?string,
     *   consent_error_message: ?string,
     *   consent_revoked_detected: bool
     * }
     */
    public function projectVerificationOutcome(ProviderConnection $connection, HealthResult $result): array
    {
        $currentConsentStatus = $this->normalizeConsentStatus($connection->consent_status) ?? ProviderConsentStatus::Unknown;

        $effectiveReasonCode = $result->healthy
            ? null
            : $this->effectiveReasonCodeForVerification($currentConsentStatus, $result->reasonCode);

        $consentStatus = $this->consentStatusAfterVerification($currentConsentStatus, $effectiveReasonCode, $result->healthy);
        $verificationStatus = $this->verificationStatusAfterVerification($effectiveReasonCode, $result);

        $consentErrorCode = in_array($consentStatus, [
            ProviderConsentStatus::Required,
            ProviderConsentStatus::Failed,
            ProviderConsentStatus::Revoked,
        ], true) ? $effectiveReasonCode : null;

        return [
            'consent_status' => $consentStatus,
            'verification_status' => $verificationStatus,
            'last_error_reason_code' => $effectiveReasonCode,
            'last_error_message' => $result->healthy ? null : $result->message,
            'consent_error_code' => $consentErrorCode,
            'consent_error_message' => $consentErrorCode === null || $result->healthy ? null : $result->message,
            'consent_revoked_detected' => $currentConsentStatus === ProviderConsentStatus::Granted
                && $effectiveReasonCode === ProviderReasonCodes::ProviderConsentRevoked,
        ];
    }

    private function normalizeConsentStatus(ProviderConsentStatus|string|null $consentStatus): ?ProviderConsentStatus
    {
        if ($consentStatus instanceof ProviderConsentStatus) {
            return $consentStatus;
        }

        if (! is_string($consentStatus)) {
            return null;
        }

        return ProviderConsentStatus::tryFrom(trim($consentStatus));
    }

    private function normalizeVerificationStatus(
        ProviderVerificationStatus|string|null $verificationStatus,
    ): ?ProviderVerificationStatus {
        if ($verificationStatus instanceof ProviderVerificationStatus) {
            return $verificationStatus;
        }

        if (! is_string($verificationStatus)) {
            return null;
        }

        return ProviderVerificationStatus::tryFrom(trim($verificationStatus));
    }

    private function effectiveReasonCodeForVerification(
        ProviderConsentStatus $currentConsentStatus,
        ?string $reasonCode,
    ): ?string {
        if (! is_string($reasonCode) || $reasonCode === '') {
            return null;
        }

        if (
            $currentConsentStatus === ProviderConsentStatus::Granted
            && $reasonCode === ProviderReasonCodes::ProviderConsentMissing
        ) {
            return ProviderReasonCodes::ProviderConsentRevoked;
        }

        return $reasonCode;
    }

    private function consentStatusAfterVerification(
        ProviderConsentStatus $currentConsentStatus,
        ?string $reasonCode,
        bool $healthy,
    ): ProviderConsentStatus {
        if ($healthy) {
            return ProviderConsentStatus::Granted;
        }

        return match ($reasonCode) {
            ProviderReasonCodes::ProviderConsentMissing => ProviderConsentStatus::Required,
            ProviderReasonCodes::ProviderConsentFailed => ProviderConsentStatus::Failed,
            ProviderReasonCodes::ProviderConsentRevoked => ProviderConsentStatus::Revoked,
            default => $currentConsentStatus,
        };
    }

    private function verificationStatusAfterVerification(
        ?string $reasonCode,
        HealthResult $result,
    ): ProviderVerificationStatus {
        if ($result->healthy) {
            return ProviderVerificationStatus::Healthy;
        }

        if (in_array($reasonCode, [
            ProviderReasonCodes::ProviderConsentMissing,
            ProviderReasonCodes::ProviderConsentFailed,
            ProviderReasonCodes::ProviderConsentRevoked,
            ProviderReasonCodes::PlatformIdentityMissing,
            ProviderReasonCodes::PlatformIdentityIncomplete,
            ProviderReasonCodes::DedicatedCredentialMissing,
            ProviderReasonCodes::DedicatedCredentialInvalid,
            ProviderReasonCodes::ProviderConnectionInvalid,
            ProviderReasonCodes::ProviderConnectionReviewRequired,
            ProviderReasonCodes::ProviderConnectionTypeInvalid,
            ProviderReasonCodes::TenantTargetMismatch,
        ], true)) {
            return ProviderVerificationStatus::Blocked;
        }

        return $this->normalizeVerificationStatus($result->verificationStatus) ?? ProviderVerificationStatus::Error;
    }
}
