<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\SupportCenterCheckSeverity;
use App\Enums\SupportCenter\SupportCenterCredentialSource;
use Illuminate\Support\Facades\Route;

/**
 * Support Center 本地自检项（不发起远程 HTTP，除非由调用方显式追加远程探测行）。
 */
final class SupportCenterLocalChecks
{
    public function __construct(
        private readonly SupportCenterRuntimeConfig $runtimeConfig,
        private readonly SupportCenterCredentialResolver $credentialResolver,
        private readonly SupportCenterReleaseCredentialProvider $releaseCredentialProvider,
        private readonly HostInstanceIdentityService $instanceIdentityService,
        private readonly HostSiteVerificationStaticPublisher $siteVerificationStaticPublisher,
        private readonly SupportCenterCheckOutputSanitizer $outputSanitizer,
    ) {}

    /**
     * @return list<SupportCenterCheckRow>
     */
    public function coreRows(bool $releasePackageMode): array
    {
        return [
            $this->supportCenterEnabledRow(),
            $this->stubModeRow(),
            $this->productCodeRow(),
            $this->releaseCredentialsRow($releasePackageMode),
            $this->baseUrlRow(),
            $this->hostTokenRow(),
            $this->wrongEnvPrefixRow(),
            $this->wellKnownRouteRow(),
            $this->wellKnownStaticFileRow(),
            $this->instanceIdentityRow(),
        ];
    }

    private function supportCenterEnabledRow(): SupportCenterCheckRow
    {
        if ($this->runtimeConfig->enabled()) {
            return new SupportCenterCheckRow(
                id: 'support_center_enabled',
                severity: SupportCenterCheckSeverity::Ok,
                label: '官方支撑中心已启用',
            );
        }

        return new SupportCenterCheckRow(
            id: 'support_center_enabled',
            severity: SupportCenterCheckSeverity::Error,
            label: '官方支撑中心未启用',
            reasonCode: SupportCenterSelfCheckReasonCodes::SUPPORT_CENTER_DISABLED,
        );
    }

    private function stubModeRow(): SupportCenterCheckRow
    {
        if (! $this->runtimeConfig->useStub()) {
            return new SupportCenterCheckRow(
                id: 'stub_mode_disabled',
                severity: SupportCenterCheckSeverity::Ok,
                label: '未启用 Stub 模式',
            );
        }

        return new SupportCenterCheckRow(
            id: 'stub_mode_disabled',
            severity: SupportCenterCheckSeverity::Error,
            label: 'Stub 模式已启用（禁止用于官方发布包）',
            reasonCode: SupportCenterSelfCheckReasonCodes::STUB_MODE_ENABLED,
        );
    }

    private function productCodeRow(): SupportCenterCheckRow
    {
        $productCode = strtolower($this->runtimeConfig->productCode());

        if ($productCode === 'dberp') {
            return new SupportCenterCheckRow(
                id: 'product_code_dberp',
                severity: SupportCenterCheckSeverity::Ok,
                label: '产品线配置为 DBErp（dberp）',
            );
        }

        return new SupportCenterCheckRow(
            id: 'product_code_dberp',
            severity: SupportCenterCheckSeverity::Error,
            label: '产品线配置不是 dberp',
            reasonCode: SupportCenterSelfCheckReasonCodes::PRODUCT_CODE_MISMATCH,
            detail: $this->outputSanitizer->sanitize($productCode),
        );
    }

    private function releaseCredentialsRow(bool $releasePackageMode): SupportCenterCheckRow
    {
        $source = $this->credentialResolver->credentialSource();
        $fileExists = $this->releaseCredentialProvider->credentialsFileExists();
        $releaseReadable = $this->releaseCredentialProvider->read() !== null;

        if ($source === SupportCenterCredentialSource::ReleasePackage && $releaseReadable) {
            return new SupportCenterCheckRow(
                id: 'release_credentials',
                severity: SupportCenterCheckSeverity::Ok,
                label: '发布包官方接入凭证已就绪',
                detail: 'credential_source=release_package',
            );
        }

        if ($source === SupportCenterCredentialSource::EnvFallback) {
            $severity = $releasePackageMode
                ? SupportCenterCheckSeverity::Error
                : SupportCenterCheckSeverity::Warn;

            return new SupportCenterCheckRow(
                id: 'release_credentials',
                severity: $severity,
                label: $releasePackageMode
                    ? '官方发布包不得依赖环境变量凭证回退'
                    : '当前使用环境变量凭证回退（仅建议开发/私有部署）',
                reasonCode: SupportCenterSelfCheckReasonCodes::ENV_FALLBACK_USED,
                detail: 'credential_source=env_fallback',
            );
        }

        if ($fileExists && ! $releaseReadable) {
            return new SupportCenterCheckRow(
                id: 'release_credentials',
                severity: SupportCenterCheckSeverity::Error,
                label: '发布包凭证文件存在但无效或仍为占位符',
                reasonCode: SupportCenterSelfCheckReasonCodes::RELEASE_CREDENTIAL_MISSING,
                detail: '[release_credentials_file_invalid]',
            );
        }

        return new SupportCenterCheckRow(
            id: 'release_credentials',
            severity: SupportCenterCheckSeverity::Error,
            label: '未找到有效的发布包官方接入凭证',
            reasonCode: SupportCenterSelfCheckReasonCodes::RELEASE_CREDENTIAL_MISSING,
            detail: $fileExists ? '[release_credentials_file_invalid]' : '[release_credentials_file_missing]',
        );
    }

    private function baseUrlRow(): SupportCenterCheckRow
    {
        $baseUrl = $this->runtimeConfig->normalizedBaseUrl();

        if ($baseUrl !== null) {
            return new SupportCenterCheckRow(
                id: 'base_url_configured',
                severity: SupportCenterCheckSeverity::Ok,
                label: '官方支撑中心服务地址已配置',
                detail: 'host='.$this->outputSanitizer->sanitizeBaseUrlForDisplay($baseUrl),
            );
        }

        return new SupportCenterCheckRow(
            id: 'base_url_configured',
            severity: SupportCenterCheckSeverity::Error,
            label: '官方支撑中心服务地址未配置或无效',
            reasonCode: SupportCenterSelfCheckReasonCodes::BASE_URL_MISSING,
        );
    }

    private function hostTokenRow(): SupportCenterCheckRow
    {
        if ($this->credentialResolver->hasHostToken()) {
            return new SupportCenterCheckRow(
                id: 'host_token_resolved',
                severity: SupportCenterCheckSeverity::Ok,
                label: '官方 Open Host 接入凭证已解析',
                detail: 'credential_source='.$this->credentialResolver->credentialSource()?->value,
            );
        }

        $blocking = $this->credentialResolver->hostTokenBlockingReasonCode();

        return new SupportCenterCheckRow(
            id: 'host_token_resolved',
            severity: SupportCenterCheckSeverity::Error,
            label: '官方 Open Host 接入凭证无法解析',
            reasonCode: SupportCenterSelfCheckReasonCodes::HOST_TOKEN_UNRESOLVED,
            detail: $blocking !== null ? 'blocking='.$blocking : null,
        );
    }

    private function wrongEnvPrefixRow(): SupportCenterCheckRow
    {
        $suspiciousKeys = $this->detectWrongEnvPrefixKeys();

        if ($suspiciousKeys === []) {
            return new SupportCenterCheckRow(
                id: 'env_prefix',
                severity: SupportCenterCheckSeverity::Ok,
                label: '未发现非 DBErp 前缀的 Support Center 环境变量',
            );
        }

        return new SupportCenterCheckRow(
            id: 'env_prefix',
            severity: SupportCenterCheckSeverity::Warn,
            label: '检测到可能误用的环境变量前缀（应使用 DBERP_SUPPORT_CENTER_*）',
            reasonCode: SupportCenterSelfCheckReasonCodes::WRONG_ENV_PREFIX_DETECTED,
            detail: $this->outputSanitizer->sanitize(implode(', ', array_slice($suspiciousKeys, 0, 5))),
        );
    }

    private function wellKnownRouteRow(): SupportCenterCheckRow
    {
        if (Route::has('dberp.well-known.site-verification')) {
            return new SupportCenterCheckRow(
                id: 'well_known_route',
                severity: SupportCenterCheckSeverity::Ok,
                label: 'well-known 站点校验路由已注册',
                detail: '/.well-known/dberp-site-verification.json',
            );
        }

        return new SupportCenterCheckRow(
            id: 'well_known_route',
            severity: SupportCenterCheckSeverity::Error,
            label: 'well-known 站点校验路由未注册',
            reasonCode: SupportCenterSelfCheckReasonCodes::WELL_KNOWN_ROUTE_MISSING,
        );
    }

    private function wellKnownStaticFileRow(): SupportCenterCheckRow
    {
        if ($this->siteVerificationStaticPublisher->isPublished()) {
            return new SupportCenterCheckRow(
                id: 'well_known_static_file',
                severity: SupportCenterCheckSeverity::Ok,
                label: 'well-known 静态校验文件已发布',
                detail: HostSiteVerificationStaticPublisher::PUBLIC_RELATIVE_PATH,
            );
        }

        return new SupportCenterCheckRow(
            id: 'well_known_static_file',
            severity: SupportCenterCheckSeverity::Warning,
            label: 'well-known 静态校验文件尚未生成',
            reasonCode: SupportCenterSelfCheckReasonCodes::WELL_KNOWN_STATIC_FILE_MISSING,
            detail: '将在安装完成、刷新接入状态或首次访问校验 URL 时自动写入 public/'.HostSiteVerificationStaticPublisher::PUBLIC_RELATIVE_PATH,
        );
    }

    private function instanceIdentityRow(): SupportCenterCheckRow
    {
        $this->instanceIdentityService->ensureProvisioned();

        if ($this->instanceIdentityService->isProvisioned()) {
            return new SupportCenterCheckRow(
                id: 'instance_identity',
                severity: SupportCenterCheckSeverity::Ok,
                label: '站点实例身份已生成（未输出密钥）',
            );
        }

        return new SupportCenterCheckRow(
            id: 'instance_identity',
            severity: SupportCenterCheckSeverity::Error,
            label: '站点实例身份尚未生成',
            reasonCode: SupportCenterSelfCheckReasonCodes::INSTANCE_IDENTITY_MISSING,
        );
    }

    /**
     * @return list<string>
     */
    private function detectWrongEnvPrefixKeys(): array
    {
        $keys = array_unique(array_merge(array_keys($_ENV), array_keys($_SERVER)));
        $suspicious = [];

        foreach ($keys as $key) {
            if (! is_string($key)) {
                continue;
            }

            $upper = strtoupper($key);

            if (str_starts_with($upper, 'DBERP_SUPPORT_CENTER_')) {
                continue;
            }

            if (str_starts_with($upper, 'SC_OPEN_HOST_') || str_starts_with($upper, 'SC_SUPPORT_CENTER_')) {
                $suspicious[] = $key;

                continue;
            }

            if (str_contains($upper, 'SUPPORT_CENTER') && str_starts_with($upper, 'DBSHOP_')) {
                $suspicious[] = $key;
            }
        }

        sort($suspicious);

        return $suspicious;
    }
}
