<?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\Services\SupportCenter\SupportCenterOpenApiTransport;
use App\Services\SupportCenter\SupportCenterOpenHostEndpoints;
use App\Services\SupportCenter\SupportCenterRequestContextBuilder;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * 从 Support Center 获取官方插件目录与详情，并归一化展示字段。
 */
final class HostPluginCatalogService
{
    public function __construct(
        private readonly SupportCenterOpenApiTransport $transport,
        private readonly SupportCenterRequestContextBuilder $contextBuilder,
        private readonly HostPluginSupportCenterContextResolver $contextResolver,
        private readonly HostPluginOperationLogger $operationLogger,
        private readonly HostPluginCatalogEmptyResponseDetector $emptyCatalogDetector,
    ) {}

    /**
     * @return array{items: list<HostPluginCatalogItem>, error_message: ?string}
     */
    public function fetchCatalog(): array
    {
        $this->operationLogger->logStarted(self::OPERATION_CATALOG_FETCH);

        try {
            $response = $this->transport->get(
                SupportCenterOpenHostEndpoints::PLUGIN_CATALOG,
                $this->contextBuilder->pluginCatalogQuery(
                    siteNo: $this->contextResolver->resolveSiteNo(),
                    licenseNo: $this->contextResolver->resolveLicenseNo(),
                ),
                'host_plugin_catalog_fetch',
            );

            $items = $this->mapCatalogItems($response);
            $this->operationLogger->logCompleted(
                self::OPERATION_CATALOG_FETCH,
                null,
                (string) __('dberp.plugin_center.activity.messages.catalog_fetch_completed', ['count' => count($items)]),
            );

            return [
                'items' => $items,
                'error_message' => null,
            ];
        } catch (SupportCenterException $exception) {
            if ($this->emptyCatalogDetector->matches($exception)) {
                return $this->completeEmptyCatalogFetch($exception);
            }

            $this->operationLogger->logFailed(
                self::OPERATION_CATALOG_FETCH,
                null,
                $exception->userFacingMessage(),
                $exception->errorCode(),
                $exception->requestId(),
            );

            return [
                'items' => [],
                'error_message' => (string) __('dberp.plugin_center.catalog.fetch_failed'),
            ];
        } catch (Throwable $exception) {
            Log::warning('dberp.plugin_center.catalog_fetch_failed', [
                'exception_class' => $exception::class,
                'exception_message' => $exception->getMessage(),
            ]);

            $this->operationLogger->logFailed(
                self::OPERATION_CATALOG_FETCH,
                null,
                (string) __('dberp.plugin_center.catalog.fetch_failed'),
                'catalog_fetch_failed',
                exception: $exception,
            );

            return [
                'items' => [],
                'error_message' => (string) __('dberp.plugin_center.catalog.fetch_failed'),
            ];
        }
    }

    public function fetchDetail(string $pluginCode): ?HostPluginCatalogItem
    {
        if (trim($pluginCode) === '') {
            return null;
        }

        try {
            $response = $this->transport->get(
                SupportCenterOpenHostEndpoints::pluginDetail($pluginCode),
                $this->contextBuilder->pluginDetailQuery(
                    siteNo: $this->contextResolver->resolveSiteNo(),
                    licenseNo: $this->contextResolver->resolveLicenseNo(),
                ),
                'host_plugin_detail_fetch',
            );

            $payload = $this->extractPluginPayload($response);

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

            return HostPluginCatalogItem::fromRemotePayload($payload);
        } catch (Throwable $exception) {
            Log::warning('dberp.plugin_center.detail_fetch_failed', [
                'plugin_code' => $pluginCode,
                'exception_class' => $exception::class,
            ]);

            return null;
        }
    }

    /**
     * @param  array<string, mixed>  $response
     * @return list<HostPluginCatalogItem>
     */
    /**
     * 支撑中心以失败信封表示「目录暂无已上架发行」时，按空目录成功处理，避免操作记录误报失败。
     *
     * @return array{items: list<HostPluginCatalogItem>, error_message: null}
     */
    private function completeEmptyCatalogFetch(SupportCenterException $exception): array
    {
        Log::info('dberp.plugin_center.catalog_fetch_empty', [
            'error_code' => $exception->errorCode(),
            'request_id' => $exception->requestId(),
        ]);

        $this->operationLogger->logCompleted(
            self::OPERATION_CATALOG_FETCH,
            null,
            (string) __('dberp.plugin_center.activity.messages.catalog_fetch_completed', ['count' => 0]),
        );

        return [
            'items' => [],
            'error_message' => null,
        ];
    }

    /**
     * @param  array<string, mixed>  $response
     * @return list<HostPluginCatalogItem>
     */
    private function mapCatalogItems(array $response): array
    {
        $rawItems = $response['plugins'] ?? $response['items'] ?? $response['catalog'] ?? [];

        if (! is_array($rawItems)) {
            return [];
        }

        $items = [];

        foreach ($rawItems as $rawItem) {
            if (! is_array($rawItem)) {
                continue;
            }

            $item = HostPluginCatalogItem::fromRemotePayload($rawItem);

            if ($item->pluginCode === '') {
                continue;
            }

            $items[] = $item;
        }

        return $items;
    }

    /**
     * @param  array<string, mixed>  $response
     * @return array<string, mixed>|null
     */
    private function extractPluginPayload(array $response): ?array
    {
        $plugin = $response['plugin'] ?? $response['data'] ?? null;

        if (is_array($plugin)) {
            return $plugin;
        }

        if (isset($response['plugin_code']) || isset($response['code'])) {
            return $response;
        }

        return null;
    }

    private const OPERATION_CATALOG_FETCH = HostPluginOperationLogger::OPERATION_CATALOG_FETCH;
}
