<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Auth\Passport;

use RuntimeException;

/**
 * OAuth 授权服务器身份。issuer 只来自配置的 APP_URL / MCP_AUTHORIZATION_SERVER，
 * 不得根据未信任的 Host 头推导。
 *
 * 安装向导常先写入 HTTP，事后再绑定证书。生产环境只把已配置主机的 http 升为 https，
 * 不改主机名，也不把请求 Host 当成 issuer。
 */
final class DberpOAuthServerIdentity
{
    public function issuer(): string
    {
        $origin = $this->configuredOrigin();
        $this->assertUsable($origin);

        return $origin;
    }

    public function absoluteUrl(string $path): string
    {
        return $this->issuer().'/'.ltrim($path, '/');
    }

    public function configuredOrigin(): string
    {
        $configured = trim((string) config('mcp.authorization_server', ''));

        if ($configured === '') {
            $configured = rtrim((string) config('app.url', ''), '/');
        }

        return $this->canonicalizeConfiguredOrigin(rtrim($configured, '/'));
    }

    /**
     * 生产环境必须是可解析的 HTTPS 源。本地 / testing 允许沿用仓库既有 HTTP 约定。
     */
    public function assertUsable(?string $origin = null): void
    {
        $origin ??= $this->configuredOrigin();
        $parts = parse_url($origin);

        if (! is_array($parts) || ! isset($parts['scheme'], $parts['host']) || $parts['host'] === '') {
            $this->failClosed('oauth_origin_malformed', $origin);
        }

        if (isset($parts['user']) || isset($parts['pass'])) {
            $this->failClosed('oauth_origin_has_userinfo', $origin);
        }

        $scheme = strtolower((string) $parts['scheme']);

        if (! in_array($scheme, ['http', 'https'], true)) {
            $this->failClosed('oauth_origin_unsupported_scheme', $origin);
        }

        if ($this->requiresHttps() && $scheme !== 'https') {
            $this->failClosed('oauth_origin_https_required', $origin);
        }
    }

    public function diagnosticCodeFor(string $origin): ?string
    {
        try {
            $this->assertUsable($origin);
        } catch (RuntimeException $exception) {
            return $exception->getMessage();
        }

        return null;
    }

    private function requiresHttps(): bool
    {
        return app()->isProduction();
    }

    /**
     * 生产环境把已配置的 http 源升为 https。主机仍来自配置，不读取请求 Host。
     * 回环地址保持原样，继续走 HTTPS 必填校验。
     */
    private function canonicalizeConfiguredOrigin(string $origin): string
    {
        if (! $this->requiresHttps() || $origin === '') {
            return $origin;
        }

        $host = strtolower((string) (parse_url($origin, PHP_URL_HOST) ?: ''));

        if ($host === '' || $this->isLoopbackHost($host)) {
            return $origin;
        }

        $canonical = preg_replace('#^http://#i', 'https://', $origin, 1);

        return is_string($canonical) ? $canonical : $origin;
    }

    private function isLoopbackHost(string $host): bool
    {
        $normalized = strtolower(trim($host, '[]'));

        return in_array($normalized, ['localhost', '127.0.0.1', '::1', '0.0.0.0'], true)
            || str_ends_with($normalized, '.localhost');
    }

    private function failClosed(string $code, string $origin): never
    {
        logger()->warning('dberp.oauth.origin_invalid', [
            'diagnostic_code' => $code,
            'origin_scheme' => parse_url($origin, PHP_URL_SCHEME),
            'origin_host' => parse_url($origin, PHP_URL_HOST),
        ]);

        throw new RuntimeException($code);
    }
}
