<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\SupportCenterCredentialSource;

/**
 * Support Center Open Host 凭证统一解析：站点级 → 发布包 → 环境变量（仅开发/私有部署）。
 */
final class SupportCenterCredentialResolver
{
    public function __construct(
        private readonly SupportCenterSiteCredentialProvider $siteCredentialProvider,
        private readonly SupportCenterReleaseCredentialProvider $releaseCredentialProvider,
    ) {}

    public function allowEnvCredentialFallback(): bool
    {
        return (bool) config('dberp.support_center.allow_env_credential_fallback', true);
    }

    public function credentialSource(): ?SupportCenterCredentialSource
    {
        return $this->resolveCredentials()['source'];
    }

    public function baseUrl(): ?string
    {
        return $this->resolveCredentials()['base_url'];
    }

    public function hostToken(): ?string
    {
        return $this->resolveCredentials()['host_token'];
    }

    public function hostKey(): ?string
    {
        return $this->resolveCredentials()['host_key'];
    }

    public function hasBaseUrl(): bool
    {
        return $this->baseUrl() !== null;
    }

    public function hasHostToken(): bool
    {
        return $this->hostToken() !== null;
    }

    public function normalizedBaseUrl(): ?string
    {
        $raw = $this->baseUrl();

        if ($raw === null) {
            return null;
        }

        if (filter_var($raw, FILTER_VALIDATE_URL) === false) {
            return null;
        }

        $scheme = strtolower((string) parse_url($raw, PHP_URL_SCHEME));

        if (! in_array($scheme, ['http', 'https'], true)) {
            return null;
        }

        return rtrim($raw, '/');
    }

    /**
     * 连通性守卫用：无有效 Open Host token 时的阻塞原因码（符号化，不含密钥）。
     */
    public function hostTokenBlockingReasonCode(): ?string
    {
        if ($this->hasHostToken()) {
            return null;
        }

        if (! $this->allowEnvCredentialFallback()) {
            return SupportCenterUserFacingErrors::RELEASE_CREDENTIAL_MISSING;
        }

        return SupportCenterUserFacingErrors::ENV_FALLBACK_MISSING;
    }

    /**
     * 连通性守卫用：无有效 base_url 时的阻塞原因码。
     */
    public function baseUrlBlockingReasonCode(): ?string
    {
        if ($this->hasBaseUrl()) {
            return null;
        }

        if (! $this->allowEnvCredentialFallback()) {
            return SupportCenterUserFacingErrors::RELEASE_CREDENTIAL_MISSING;
        }

        return SupportCenterUserFacingErrors::ENV_FALLBACK_MISSING;
    }

    /**
     * @return array{
     *     has_host_token: bool,
     *     has_host_key: bool,
     *     has_base_url: bool,
     *     credential_source: ?string,
     * }
     */
    public function credentialPresenceForLogs(): array
    {
        return [
            'has_host_token' => $this->hasHostToken(),
            'has_host_key' => $this->hostKey() !== null,
            'has_base_url' => $this->hasBaseUrl(),
            'credential_source' => $this->credentialSource()?->value,
        ];
    }

    /**
     * 技术支持诊断（符号化原因码，不含 token 明文）。
     *
     * @return array<string, mixed>
     */
    public function technicalCredentialDiagnostics(): array
    {
        return [
            'credential_source' => $this->credentialSource()?->value,
            'has_host_token' => $this->hasHostToken(),
            'has_host_key' => $this->hostKey() !== null,
            'has_base_url' => $this->hasBaseUrl(),
            'release_credentials_file_exists' => $this->releaseCredentialProvider->credentialsFileExists(),
            'allow_env_credential_fallback' => $this->allowEnvCredentialFallback(),
            'host_token_blocking_reason' => $this->hostTokenBlockingReasonCode(),
            'base_url_blocking_reason' => $this->baseUrlBlockingReasonCode(),
        ];
    }

    /**
     * @return array{
     *     source: ?SupportCenterCredentialSource,
     *     base_url: ?string,
     *     host_token: ?string,
     *     host_key: ?string,
     * }
     */
    private function resolveCredentials(): array
    {
        $site = $this->siteCredentialProvider->read();

        if ($site !== null) {
            return $this->mapResolved($site, SupportCenterCredentialSource::SiteStored);
        }

        $release = $this->releaseCredentialProvider->read();

        if ($release !== null) {
            return $this->mapResolved($release, SupportCenterCredentialSource::ReleasePackage);
        }

        if ($this->allowEnvCredentialFallback()) {
            $envBaseUrl = $this->envBaseUrl();
            $envHostToken = $this->envHostToken();
            $envHostKey = $this->envHostKey();

            if ($envBaseUrl !== null || $envHostToken !== null || $envHostKey !== null) {
                return $this->mapResolved([
                    'base_url' => $envBaseUrl ?? '',
                    'host_token' => $envHostToken ?? '',
                    'host_key' => $envHostKey,
                ], SupportCenterCredentialSource::EnvFallback);
            }
        }

        return [
            'source' => null,
            'base_url' => null,
            'host_token' => null,
            'host_key' => null,
        ];
    }

    /**
     * @param  array{base_url: string, host_token: string, host_key: ?string}  $credentials
     * @return array{
     *     source: SupportCenterCredentialSource,
     *     base_url: ?string,
     *     host_token: ?string,
     *     host_key: ?string,
     * }
     */
    private function mapResolved(array $credentials, SupportCenterCredentialSource $source): array
    {
        return [
            'source' => $source,
            'base_url' => trim($credentials['base_url']) !== '' ? trim($credentials['base_url']) : null,
            'host_token' => trim($credentials['host_token']) !== '' ? trim($credentials['host_token']) : null,
            'host_key' => isset($credentials['host_key']) && is_string($credentials['host_key']) && trim($credentials['host_key']) !== ''
                ? trim($credentials['host_key'])
                : null,
        ];
    }

    private function envBaseUrl(): ?string
    {
        return $this->trimmedConfigString('dberp.support_center.base_url');
    }

    private function envHostToken(): ?string
    {
        return $this->trimmedConfigString('dberp.support_center.host_token');
    }

    private function envHostKey(): ?string
    {
        return $this->trimmedConfigString('dberp.support_center.host_key');
    }

    public function hasEnvHostTokenConfigured(): bool
    {
        return $this->envHostToken() !== null;
    }

    public function hasEnvBaseUrlConfigured(): bool
    {
        return $this->envBaseUrl() !== null;
    }

    private function trimmedConfigString(string $key): ?string
    {
        $value = config($key);

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

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }
}
