<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Exceptions\SupportCenter\HostSystemUpdatePostApplyException;
use App\Models\HostSystemUpdateExecution;
use App\Services\Install\EnvironmentFileWriter;
use App\Support\DberpProductVersion;
use RuntimeException;

/**
 * 写入 DBERP_DISPLAY_VERSION / DBERP_UPDATE_VERSION（保留其他 .env 键）。
 */
final class HostSystemUpdateVersionWriter
{
    public const ENV_DISPLAY_VERSION = 'DBERP_DISPLAY_VERSION';

    public const ENV_UPDATE_VERSION = 'DBERP_UPDATE_VERSION';

    public function __construct(
        private readonly HostSystemUpdateExecutionContextResolver $contextResolver,
        private readonly EnvironmentFileWriter $environmentFileWriter,
        private readonly DberpProductVersion $productVersion,
        private readonly HostSystemUpdateExecutionLogger $executionLogger,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    /**
     * @return array{display_version: string, update_version: int, previous_display_version: string, previous_update_version: int}
     */
    public function writeVersion(HostSystemUpdateExecution $execution): array
    {
        $startedAt = now()->utc();
        $package = $this->contextResolver->resolveReadyPackage($execution);
        $manifest = $this->contextResolver->manifest($execution);

        $previousDisplay = $this->productVersion->displayVersion();
        $previousUpdate = $this->productVersion->updateVersion();
        [$targetDisplay, $targetUpdate] = $this->resolveTargetVersions($manifest, $execution);

        $this->auditLogger->logSystemUpdateVersionWriteStarted(
            $execution,
            $package,
            $previousDisplay,
            $previousUpdate,
            $targetDisplay,
            $targetUpdate,
        );
        $this->executionLogger->logStepStarted(
            $execution,
            HostSystemUpdateExecutionStep::WriteVersion->value,
            (string) __('dberp.system_update.post_apply.log.version_write_started'),
            [
                'from_display_version' => $previousDisplay,
                'from_update_version' => $previousUpdate,
                'to_display_version' => $targetDisplay,
                'to_update_version' => $targetUpdate,
            ],
        );

        try {
            $this->environmentFileWriter->mergeKeysIntoEnv(base_path(), [
                self::ENV_DISPLAY_VERSION => $targetDisplay,
                self::ENV_UPDATE_VERSION => $targetUpdate,
            ]);

            config([
                'dberp.version.display_version' => $targetDisplay,
                'dberp.version.update_version' => $targetUpdate,
                'dberp.public_product.version' => $targetDisplay,
            ]);

            $summary = [
                'display_version' => $targetDisplay,
                'update_version' => $targetUpdate,
                'previous_display_version' => $previousDisplay,
                'previous_update_version' => $previousUpdate,
            ];

            $this->executionLogger->logStepCompleted(
                $execution,
                HostSystemUpdateExecutionStep::WriteVersion->value,
                (string) __('dberp.system_update.post_apply.log.version_write_completed'),
                $summary,
                $startedAt,
            );

            $this->auditLogger->logSystemUpdateVersionWriteCompleted($execution, $package, $summary);

            return $summary;
        } catch (RuntimeException $exception) {
            $postApplyException = HostSystemUpdatePostApplyException::versionWriteFailed($exception->getMessage());

            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::WriteVersion->value,
                $postApplyException->userFacingMessage(),
                $postApplyException->errorCode(),
                $postApplyException->userFacingMessage(),
                [
                    'from_display_version' => $previousDisplay,
                    'from_update_version' => $previousUpdate,
                ],
                $startedAt,
            );
            $this->auditLogger->logSystemUpdateVersionWriteFailed($execution, $package, $postApplyException);

            throw $postApplyException;
        }
    }

    /**
     * @param  array<string, mixed>  $manifest
     * @return array{0: string, 1: int}
     */
    private function resolveTargetVersions(array $manifest, HostSystemUpdateExecution $execution): array
    {
        $display = $manifest[HostSystemUpdateManifestContract::TO_VERSION] ?? $execution->to_version;
        $update = $manifest[HostSystemUpdateManifestContract::TO_UPDATE_VERSION] ?? $execution->to_update_version;

        if (! is_string($display) || trim($display) === '' || ! is_int($update)) {
            throw HostSystemUpdatePostApplyException::manifestVersionMissing();
        }

        return [trim($display), $update];
    }
}
