<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Exceptions\SupportCenter\HostSystemUpdateManifestException;
use App\Models\HostSystemUpdatePackage;
use DomainException;

/**
 * 读取并校验已下载更新包的 manifest.json，持久化解析结果。
 */
final class HostSystemUpdateManifestParseService
{
    public function __construct(
        private readonly HostSystemUpdateManifestReader $manifestReader,
        private readonly HostSystemUpdateManifestValidator $manifestValidator,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    public function parse(?HostSystemUpdatePackage $package = null): HostSystemUpdatePackage
    {
        $package ??= $this->resolveLatestVerifiedPackage();

        $this->auditLogger->logSystemUpdateManifestParsingStarted($package);

        try {
            $manifest = $this->manifestReader->readFromPackage($package);
            $this->manifestValidator->validate($manifest, $package);

            $package->fill([
                'manifest' => $manifest,
                'manifest_status' => HostSystemUpdatePackage::MANIFEST_STATUS_VALID,
                'manifest_message' => null,
                'manifest_error_code' => null,
                'manifest_parsed_at' => now()->utc(),
            ]);
            $package->save();

            $this->auditLogger->logSystemUpdateManifestParsingCompleted($package->fresh() ?? $package);

            return $package->fresh() ?? $package;
        } catch (HostSystemUpdateManifestException $exception) {
            $this->persistManifestFailure($package, $exception);

            if ($this->isParseFailure($exception)) {
                $this->auditLogger->logSystemUpdateManifestParsingFailed($package->fresh() ?? $package, $exception);
            } else {
                $this->auditLogger->logSystemUpdateManifestValidationFailed($package->fresh() ?? $package, $exception);
            }

            throw $exception;
        }
    }

    /**
     * @return list<string>
     */
    private function parseFailureCodes(): array
    {
        return [
            'manifest_package_not_verified',
            'manifest_package_file_missing',
            'manifest_missing',
            'manifest_invalid_json',
            'manifest_zip_open_failed',
        ];
    }

    private function isParseFailure(HostSystemUpdateManifestException $exception): bool
    {
        return in_array($exception->errorCode(), $this->parseFailureCodes(), true);
    }

    private function resolveLatestVerifiedPackage(): HostSystemUpdatePackage
    {
        $package = HostSystemUpdatePackage::query()
            ->where('verified', true)
            ->where('download_status', HostSystemUpdatePackage::STATUS_VERIFIED)
            ->orderByDesc('id')
            ->first();

        if ($package instanceof HostSystemUpdatePackage) {
            return $package;
        }

        throw new DomainException((string) __('dberp.system_update.manifest.no_verified_package'));
    }

    private function persistManifestFailure(
        HostSystemUpdatePackage $package,
        HostSystemUpdateManifestException $exception,
    ): void {
        $status = $this->resolveFailureStatus($exception);

        $package->fill([
            'manifest_status' => $status,
            'manifest_message' => $exception->userFacingMessage(),
            'manifest_error_code' => $exception->errorCode(),
            'manifest_parsed_at' => now()->utc(),
        ]);
        $package->save();
    }

    private function resolveFailureStatus(HostSystemUpdateManifestException $exception): string
    {
        return match ($exception->errorCode()) {
            'manifest_invalid_json',
            'manifest_missing',
            'manifest_zip_open_failed',
            'manifest_package_file_missing' => HostSystemUpdatePackage::MANIFEST_STATUS_PARSE_FAILED,
            'manifest_package_type_unsupported' => HostSystemUpdatePackage::MANIFEST_STATUS_UNSUPPORTED,
            default => HostSystemUpdatePackage::MANIFEST_STATUS_INVALID,
        };
    }
}
