<?php

declare(strict_types=1);

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

namespace App\Services\Plugin;

use App\Exceptions\SupportCenter\SupportCenterException;
use App\Models\HostPluginPackage;
use App\Services\SupportCenter\SupportCenterOpenApiTransport;
use App\Services\SupportCenter\SupportCenterOpenHostEndpoints;
use App\Services\SupportCenter\SupportCenterRequestContextBuilder;

/**
 * 向 Support Center 申请官方插件安装包下载令牌。
 */
final class HostPluginDownloadTokenService
{
    public function __construct(
        private readonly SupportCenterOpenApiTransport $transport,
        private readonly SupportCenterRequestContextBuilder $contextBuilder,
        private readonly HostPluginSupportCenterContextResolver $contextResolver,
        private readonly HostPluginEntitlementGate $entitlementGate,
        private readonly HostPluginOperationLogger $operationLogger,
    ) {}

    public function requestToken(
        HostPluginClaimCheckResult $claimResult,
        HostPluginRemotePackagePurpose $purpose = HostPluginRemotePackagePurpose::Install,
    ): HostPluginDownloadTokenResult {
        $this->assertEntitlementForPurpose($purpose);

        $pluginCode = $claimResult->pluginCode;
        $version = $claimResult->version;

        $this->operationLogger->logStarted(
            HostPluginOperationLogger::OPERATION_DOWNLOAD_TOKEN,
            $pluginCode,
            ['version' => $version],
        );

        try {
            $response = $this->transport->post(
                SupportCenterOpenHostEndpoints::pluginDownloadToken($pluginCode),
                $this->contextBuilder->pluginDownloadTokenBody(
                    version: $version,
                    siteNo: $this->contextResolver->resolveSiteNo(),
                    licenseNo: $this->contextResolver->resolveLicenseNo(),
                ),
                'host_plugin_download_token',
            );

            $downloadToken = $this->nullableString($response['download_token'] ?? null);

            if ($downloadToken === null) {
                throw SupportCenterException::invalidResponseFormat(operation: 'host_plugin_download_token');
            }

            $package = $this->persistPackageMetadata($response, $claimResult);
            $requestId = $this->nullableString($response['request_id'] ?? null);

            $this->operationLogger->logCompleted(
                HostPluginOperationLogger::OPERATION_DOWNLOAD_TOKEN,
                $pluginCode,
                null,
                ['version' => $version, 'request_id' => $requestId],
            );

            return new HostPluginDownloadTokenResult(
                downloadToken: $downloadToken,
                downloadUrl: $this->nullableString($response['download_url'] ?? null),
                package: $package,
                requestId: $requestId,
            );
        } catch (SupportCenterException $exception) {
            $this->operationLogger->logFailed(
                HostPluginOperationLogger::OPERATION_DOWNLOAD_TOKEN,
                $pluginCode,
                $exception->userFacingMessage(),
                $exception->errorCode(),
                $exception->requestId(),
                ['version' => $version],
            );

            throw $exception;
        }
    }

    private function assertEntitlementForPurpose(HostPluginRemotePackagePurpose $purpose): void
    {
        if ($purpose === HostPluginRemotePackagePurpose::Update) {
            if (! $this->entitlementGate->canUpdatePlugin()) {
                throw SupportCenterException::entitlementBlocked(
                    userFacingMessage: $this->entitlementGate->updateReason(),
                    operation: 'host_plugin_download_token',
                );
            }

            return;
        }

        if (! $this->entitlementGate->canInstallPlugin()) {
            throw SupportCenterException::entitlementBlocked(
                userFacingMessage: $this->entitlementGate->installReason(),
                operation: 'host_plugin_download_token',
            );
        }
    }

    /**
     * @param  array<string, mixed>  $response
     */
    private function persistPackageMetadata(array $response, HostPluginClaimCheckResult $claimResult): HostPluginPackage
    {
        $metadata = $claimResult->packageMetadata;
        $packageSection = is_array($response['package'] ?? null) ? $response['package'] : [];

        $attributes = [
            'plugin_code' => $claimResult->pluginCode,
            'version' => $claimResult->version,
            'package_id' => $this->nullableString($packageSection['package_id'] ?? $metadata['package_id'] ?? $response['package_id'] ?? null),
            'package_name' => $this->nullableString($packageSection['package_name'] ?? $metadata['package_name'] ?? $response['package_name'] ?? null),
            'package_size' => $this->nullableInt($packageSection['package_size'] ?? $metadata['package_size'] ?? $response['package_size'] ?? null),
            'checksum' => $this->nullableString(
                $packageSection['checksum']
                ?? $packageSection['sha256']
                ?? $response['checksum']
                ?? $response['sha256']
                ?? $metadata['checksum']
                ?? $metadata['sha256']
                ?? null,
            ),
            'checksum_algorithm' => $this->nullableString(
                $packageSection['checksum_algorithm']
                ?? $response['checksum_algorithm']
                ?? $metadata['checksum_algorithm']
                ?? 'sha256',
            ),
            'signature' => $this->nullableString(
                $packageSection['signature']
                ?? $response['signature']
                ?? $metadata['signature']
                ?? null,
            ),
            'raw_payload' => array_filter([
                'claim' => [
                    'plugin_code' => $claimResult->pluginCode,
                    'version' => $claimResult->version,
                ],
            ]),
        ];

        /** @var HostPluginPackage $package */
        $package = HostPluginPackage::query()->updateOrCreate(
            [
                'plugin_code' => $claimResult->pluginCode,
                'version' => $claimResult->version,
            ],
            array_merge($attributes, [
                'download_status' => HostPluginPackage::STATUS_TOKEN_ISSUED,
                'download_message' => null,
                'download_error_code' => null,
                'download_request_id' => $this->nullableString($response['request_id'] ?? $claimResult->requestId),
                'verified' => false,
                'verified_at' => null,
            ]),
        );

        return $package->fresh() ?? $package;
    }

    private function nullableString(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $trimmed = trim($value);

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

    private function nullableInt(mixed $value): ?int
    {
        if (is_int($value)) {
            return $value;
        }

        if (is_string($value) && ctype_digit(trim($value))) {
            return (int) trim($value);
        }

        return null;
    }
}
