<?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>  $contextualIdentityDetails
     */
    private function __construct(
        public readonly bool $resolved,
        public readonly ProviderConnectionType $connectionType,
        public readonly string $tenantContext,
        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 ?ProviderConnectionTargetScopeDescriptor $targetScope,
        public readonly array $contextualIdentityDetails,
    ) {}

    public static function resolved(
        ProviderConnectionType $connectionType,
        string $tenantContext,
        string $effectiveClientId,
        string $credentialSource,
        ?string $clientSecret,
        ?string $authorityTenant,
        ?string $redirectUri,
        ?ProviderConnectionTargetScopeDescriptor $targetScope = null,
        array $contextualIdentityDetails = [],
    ): self {
        return new self(
            resolved: true,
            connectionType: $connectionType,
            tenantContext: $tenantContext,
            effectiveClientId: $effectiveClientId,
            credentialSource: $credentialSource,
            clientSecret: $clientSecret,
            authorityTenant: $authorityTenant,
            redirectUri: $redirectUri,
            reasonCode: null,
            message: null,
            targetScope: $targetScope ?? self::targetScopeFromContext($tenantContext),
            contextualIdentityDetails: $contextualIdentityDetails !== []
                ? $contextualIdentityDetails
                : self::contextualIdentityDetails($tenantContext, $authorityTenant, $redirectUri),
        );
    }

    public static function blocked(
        ProviderConnectionType $connectionType,
        string $tenantContext,
        string $credentialSource,
        string $reasonCode,
        ?string $message = null,
        ?ProviderConnectionTargetScopeDescriptor $targetScope = null,
        array $contextualIdentityDetails = [],
    ): self {
        return new self(
            resolved: false,
            connectionType: $connectionType,
            tenantContext: $tenantContext,
            effectiveClientId: null,
            credentialSource: $credentialSource,
            clientSecret: null,
            authorityTenant: null,
            redirectUri: null,
            reasonCode: ProviderReasonCodes::isKnown($reasonCode) ? $reasonCode : ProviderReasonCodes::UnknownError,
            message: $message,
            targetScope: $targetScope ?? (trim($tenantContext) !== '' ? self::targetScopeFromContext($tenantContext) : null),
            contextualIdentityDetails: $contextualIdentityDetails !== []
                ? $contextualIdentityDetails
                : self::contextualIdentityDetails($tenantContext),
        );
    }

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

    private static function targetScopeFromContext(string $tenantContext): ProviderConnectionTargetScopeDescriptor
    {
        $identifier = trim($tenantContext) !== '' ? trim($tenantContext) : 'organizations';

        return ProviderConnectionTargetScopeDescriptor::fromInput(
            provider: 'microsoft',
            scopeKind: ProviderConnectionTargetScopeDescriptor::SCOPE_KIND_TENANT,
            scopeIdentifier: $identifier,
            scopeDisplayName: $identifier,
        );
    }

    /**
     * @return list<ProviderIdentityContextMetadata>
     */
    private static function contextualIdentityDetails(
        string $tenantContext,
        ?string $authorityTenant = null,
        ?string $redirectUri = null,
    ): array {
        $details = [
            ProviderIdentityContextMetadata::microsoftTenantId($tenantContext),
            ProviderIdentityContextMetadata::authorityTenant($authorityTenant),
            ProviderIdentityContextMetadata::redirectUri($redirectUri),
        ];

        return array_values(array_filter(
            $details,
            static fn (?ProviderIdentityContextMetadata $detail): bool => $detail instanceof ProviderIdentityContextMetadata,
        ));
    }
}
