<?php

namespace App\Services\Providers;

use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Support\Providers\ProviderConnectionType;
use App\Support\Providers\ProviderCredentialSource;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeDescriptor;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeNormalizer;
use InvalidArgumentException;
use RuntimeException;

final class ProviderIdentityResolver
{
    public function __construct(
        private readonly PlatformProviderIdentityResolver $platformResolver,
        private readonly CredentialManager $credentials,
        private readonly ProviderConnectionTargetScopeNormalizer $targetScopeNormalizer,
    ) {}

    public function resolve(ProviderConnection $connection): ProviderIdentityResolution
    {
        $connectionType = $this->resolveConnectionType($connection);
        $targetScopeResult = $this->targetScopeNormalizer->normalizeConnection($connection);
        $targetScope = $targetScopeResult['target_scope'] ?? null;
        $providerContextDetails = $this->targetScopeNormalizer->contextualIdentityDetailsForConnection($connection);
        $targetScopeIdentifier = $targetScope instanceof ProviderConnectionTargetScopeDescriptor
            ? trim((string) $targetScope->scopeIdentifier)
            : '';

        if ($connectionType === null) {
            return ProviderIdentityResolution::blocked(
                connectionType: ProviderConnectionType::Platform,
                credentialSource: 'unknown',
                reasonCode: ProviderReasonCodes::ProviderConnectionTypeInvalid,
                message: 'Provider connection type is invalid.',
                targetScope: $targetScope instanceof ProviderConnectionTargetScopeDescriptor ? $targetScope : null,
                providerContextDetails: $providerContextDetails,
            );
        }

        if ($targetScopeResult['status'] !== ProviderConnectionTargetScopeNormalizer::STATUS_NORMALIZED) {
            return ProviderIdentityResolution::blocked(
                connectionType: $connectionType,
                credentialSource: $connectionType === ProviderConnectionType::Platform ? 'platform_config' : ProviderCredentialSource::DedicatedManual->value,
                reasonCode: ProviderReasonCodes::ProviderConnectionInvalid,
                message: $targetScopeResult['message'] ?? 'Provider connection target scope is invalid.',
                targetScope: $targetScope instanceof ProviderConnectionTargetScopeDescriptor ? $targetScope : null,
                providerContextDetails: $providerContextDetails,
            );
        }

        if ((bool) $connection->migration_review_required) {
            return ProviderIdentityResolution::blocked(
                connectionType: $connectionType,
                credentialSource: $connectionType === ProviderConnectionType::Platform ? 'platform_config' : ProviderCredentialSource::LegacyMigrated->value,
                reasonCode: ProviderReasonCodes::ProviderConnectionReviewRequired,
                message: 'Provider connection requires migration review before use.',
                targetScope: $targetScope instanceof ProviderConnectionTargetScopeDescriptor ? $targetScope : null,
                providerContextDetails: $providerContextDetails,
            );
        }

        if ($connectionType === ProviderConnectionType::Platform) {
            return $this->platformResolver->resolve(
                targetScopeIdentifier: $targetScopeIdentifier,
                targetScope: $targetScope instanceof ProviderConnectionTargetScopeDescriptor ? $targetScope : null,
                providerContextDetails: $providerContextDetails,
            );
        }

        return $this->resolveDedicatedIdentity(
            connection: $connection,
            targetScopeIdentifier: $targetScopeIdentifier,
            targetScope: $targetScope instanceof ProviderConnectionTargetScopeDescriptor ? $targetScope : null,
            providerContextDetails: $providerContextDetails,
        );
    }

    private function resolveConnectionType(ProviderConnection $connection): ?ProviderConnectionType
    {
        $value = $connection->connection_type;

        if ($value instanceof ProviderConnectionType) {
            return $value;
        }

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

        return ProviderConnectionType::tryFrom(trim($value));
    }

    private function resolveDedicatedIdentity(
        ProviderConnection $connection,
        string $targetScopeIdentifier,
        ?ProviderConnectionTargetScopeDescriptor $targetScope = null,
        array $providerContextDetails = [],
    ): ProviderIdentityResolution {
        try {
            $credentials = $this->credentials->getClientCredentials($connection);
        } catch (InvalidArgumentException|RuntimeException $exception) {
            return ProviderIdentityResolution::blocked(
                connectionType: ProviderConnectionType::Dedicated,
                credentialSource: $this->credentialSource($connection),
                reasonCode: $exception instanceof InvalidArgumentException
                    ? ProviderReasonCodes::DedicatedCredentialInvalid
                    : ProviderReasonCodes::DedicatedCredentialMissing,
                message: $exception->getMessage(),
                targetScope: $targetScope,
                providerContextDetails: $providerContextDetails,
            );
        }

        if (! $targetScope instanceof ProviderConnectionTargetScopeDescriptor) {
            $targetScope = ProviderConnectionTargetScopeDescriptor::fromInput(
                provider: 'microsoft',
                scopeKind: ProviderConnectionTargetScopeDescriptor::SCOPE_KIND_TENANT,
                scopeIdentifier: $targetScopeIdentifier !== '' ? $targetScopeIdentifier : $connection->tenant?->providerTenantContext() ?? 'organizations',
            );
        }

        return ProviderIdentityResolution::resolved(
            connectionType: ProviderConnectionType::Dedicated,
            targetScope: $targetScope,
            effectiveClientId: $credentials['client_id'],
            credentialSource: $this->credentialSource($connection),
            clientSecret: $credentials['client_secret'],
            authorityTenant: $targetScope->scopeIdentifier,
            redirectUri: trim((string) route('admin.consent.callback')),
            providerContextDetails: $providerContextDetails,
        );
    }

    private function credentialSource(ProviderConnection $connection): string
    {
        $credential = $connection->credential;

        if (! $credential instanceof ProviderCredential) {
            return ProviderCredentialSource::DedicatedManual->value;
        }

        $source = $credential->source;

        if ($source instanceof ProviderCredentialSource) {
            return $source->value;
        }

        if (is_string($source) && $source !== '') {
            return $source;
        }

        return ProviderCredentialSource::DedicatedManual->value;
    }
}
