<?php

declare(strict_types=1);

namespace App\Services\Providers;

use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Support\Providers\ProviderConnectionType;
use App\Support\Providers\ProviderConsentStatus;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\ProviderVerificationStatus;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;

final class ProviderConnectionMutationService
{
    public function __construct(private readonly CredentialManager $credentials) {}

    public function enableDedicatedOverride(
        ProviderConnection $connection,
        string $clientId,
        string $clientSecret,
    ): ProviderConnection {
        $clientId = trim($clientId);
        $clientSecret = trim($clientSecret);

        if ($clientId === '' || $clientSecret === '') {
            throw new InvalidArgumentException('Dedicated app (client) ID and client secret are required.');
        }

        return DB::transaction(function () use ($connection, $clientId, $clientSecret): ProviderConnection {
            $connection->loadMissing('credential');

            $credential = $connection->credential;
            $payload = $credential instanceof ProviderCredential && is_array($credential->payload)
                ? $credential->payload
                : [];

            $previousClientId = trim((string) ($payload['client_id'] ?? ''));
            $needsConsentReset = $connection->connection_type !== ProviderConnectionType::Dedicated
                || $previousClientId === ''
                || $previousClientId !== $clientId;

            $consentStatus = $needsConsentReset
                ? ProviderConsentStatus::Required
                : $this->normalizeConsentStatus($connection->consent_status);
            $verificationStatus = ProviderVerificationStatus::Unknown;

            $connection->forceFill([
                'connection_type' => ProviderConnectionType::Dedicated->value,
                'consent_status' => $consentStatus->value,
                'verification_status' => $verificationStatus->value,
                'last_health_check_at' => null,
                'last_error_reason_code' => $needsConsentReset
                    ? ProviderReasonCodes::ProviderConsentMissing
                    : null,
                'last_error_message' => null,
                'scopes_granted' => $needsConsentReset ? [] : ($connection->scopes_granted ?? []),
                'consent_granted_at' => $needsConsentReset ? null : $connection->consent_granted_at,
                'consent_last_checked_at' => $needsConsentReset ? null : $connection->consent_last_checked_at,
                'consent_error_code' => null,
                'consent_error_message' => null,
            ])->save();

            $this->credentials->upsertClientSecretCredential(
                connection: $connection->fresh(),
                clientId: $clientId,
                clientSecret: $clientSecret,
            );

            return $connection->fresh(['credential']);
        });
    }

    public function revertToPlatform(ProviderConnection $connection): ProviderConnection
    {
        return DB::transaction(function () use ($connection): ProviderConnection {
            $connection->loadMissing('credential');

            if ($connection->credential instanceof ProviderCredential) {
                $connection->credential->delete();
            }

            $connection->forceFill([
                'connection_type' => ProviderConnectionType::Platform->value,
                'consent_status' => ProviderConsentStatus::Required->value,
                'consent_granted_at' => null,
                'consent_last_checked_at' => null,
                'consent_error_code' => null,
                'consent_error_message' => null,
                'verification_status' => ProviderVerificationStatus::Unknown->value,
                'last_health_check_at' => null,
                'last_error_reason_code' => ProviderReasonCodes::ProviderConsentMissing,
                'last_error_message' => null,
                'scopes_granted' => [],
            ])->save();

            return $connection->fresh(['credential']);
        });
    }

    public function deleteDedicatedCredential(ProviderConnection $connection): ProviderConnection
    {
        return DB::transaction(function () use ($connection): ProviderConnection {
            $connection->loadMissing('credential');

            if ($connection->credential instanceof ProviderCredential) {
                $connection->credential->delete();
            }

            $consentStatus = $this->normalizeConsentStatus($connection->consent_status);
            $verificationStatus = ProviderVerificationStatus::Blocked;

            $connection->forceFill([
                'connection_type' => ProviderConnectionType::Dedicated->value,
                'consent_status' => $consentStatus->value,
                'verification_status' => $verificationStatus->value,
                'last_health_check_at' => null,
                'last_error_reason_code' => ProviderReasonCodes::DedicatedCredentialMissing,
                'last_error_message' => 'Dedicated credential is missing.',
            ])->save();

            return $connection->fresh(['credential']);
        });
    }

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

        if (is_string($consentStatus)) {
            return ProviderConsentStatus::tryFrom(trim($consentStatus)) ?? ProviderConsentStatus::Required;
        }

        return ProviderConsentStatus::Required;
    }
}
