<?php

namespace App\Services\Providers;

use App\Support\Providers\ProviderConnectionType;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeDescriptor;
use App\Support\Providers\TargetScope\ProviderIdentityContextMetadata;

final class ProviderIdentityResolution
{
    /**
     * @param  list<ProviderIdentityContextMetadata>  $providerContextDetails
     */
    private function __construct(
        public readonly bool $resolved,
        public readonly ProviderConnectionType $connectionType,
        public readonly ?ProviderConnectionTargetScopeDescriptor $targetScope,
        public readonly ?string $effectiveClientId,
        public readonly string $credentialSource,
        public readonly ?string $clientSecret,
        public readonly ?string $authorityTenant,
        public readonly ?string $redirectUri,
        public readonly ?string $reasonCode,
        public readonly ?string $message,
        public readonly array $providerContextDetails,
    ) {}

    /**
     * @param  list<ProviderIdentityContextMetadata>  $providerContextDetails
     */
    public static function resolved(
        ProviderConnectionType $connectionType,
        ProviderConnectionTargetScopeDescriptor $targetScope,
        string $effectiveClientId,
        string $credentialSource,
        ?string $clientSecret,
        ?string $authorityTenant,
        ?string $redirectUri,
        array $providerContextDetails = [],
    ): self {
        return new self(
            resolved: true,
            connectionType: $connectionType,
            targetScope: $targetScope,
            effectiveClientId: $effectiveClientId,
            credentialSource: $credentialSource,
            clientSecret: $clientSecret,
            authorityTenant: $authorityTenant,
            redirectUri: $redirectUri,
            reasonCode: null,
            message: null,
            providerContextDetails: $providerContextDetails,
        );
    }

    /**
     * @param  list<ProviderIdentityContextMetadata>  $providerContextDetails
     */
    public static function blocked(
        ProviderConnectionType $connectionType,
        string $credentialSource,
        string $reasonCode,
        ?string $message = null,
        ?ProviderConnectionTargetScopeDescriptor $targetScope = null,
        array $providerContextDetails = [],
    ): self {
        return new self(
            resolved: false,
            connectionType: $connectionType,
            targetScope: $targetScope,
            effectiveClientId: null,
            credentialSource: $credentialSource,
            clientSecret: null,
            authorityTenant: null,
            redirectUri: null,
            reasonCode: ProviderReasonCodes::isKnown($reasonCode) ? $reasonCode : ProviderReasonCodes::UnknownError,
            message: $message,
            providerContextDetails: $providerContextDetails,
        );
    }

    public function effectiveReasonCode(): string
    {
        return $this->reasonCode ?? ProviderReasonCodes::UnknownError;
    }

    public function targetScopeIdentifier(?string $fallback = null): ?string
    {
        $identifier = trim((string) $this->targetScope?->scopeIdentifier);

        if ($identifier !== '') {
            return $identifier;
        }

        return $fallback;
    }

    /**
     * @return array{client_id: ?string, credential_source: string}
     */
    public function effectiveClientIdentity(): array
    {
        return [
            'client_id' => $this->effectiveClientId,
            'credential_source' => $this->credentialSource,
        ];
    }

    /**
     * @return array{provider: string, details: list<array<string, string>>}
     */
    public function providerContext(): array
    {
        $provider = $this->targetScope?->provider;

        if (! is_string($provider) || trim($provider) === '') {
            foreach ($this->providerContextDetails as $detail) {
                if (trim($detail->provider) !== '') {
                    $provider = $detail->provider;

                    break;
                }
            }
        }

        return [
            'provider' => is_string($provider) && trim($provider) !== '' ? trim($provider) : 'unknown',
            'details' => array_map(
                static fn (ProviderIdentityContextMetadata $detail): array => $detail->toArray(),
                $this->providerContextDetails,
            ),
        ];
    }
}
