<?php

declare(strict_types=1);

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

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Exceptions\SupportCenter\HostSystemUpdateRecoveryException;
use App\Models\HostSystemUpdateBackup;
use App\Models\HostSystemUpdateExecution;
use App\Services\Install\EnvironmentFileWriter;
use RuntimeException;

/**
 * 回滚时恢复 DBERP 版本键；可选恢复 .env 快照（默认关闭）。
 */
final class HostSystemUpdateEnvRestoreService
{
    public function __construct(
        private readonly EnvironmentFileWriter $environmentFileWriter,
        private readonly HostSystemUpdateExecutionLogger $executionLogger,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    /**
     * @return array{display_version: string, update_version: int, env_snapshot_restored: bool}
     */
    public function restoreVersionKeys(HostSystemUpdateExecution $execution): array
    {
        $startedAt = now()->utc();
        $display = (string) ($execution->from_version ?? '');
        $update = (int) ($execution->from_update_version ?? 0);

        if ($display === '' || $update <= 0) {
            throw HostSystemUpdateRecoveryException::envRestoreFailed('previous_version_missing');
        }

        $this->executionLogger->logStepStarted(
            $execution,
            HostSystemUpdateExecutionStep::Rollback->value,
            (string) __('dberp.system_update.recovery.log.env_restore_started'),
            [
                'target_display_version' => $display,
                'target_update_version' => $update,
            ],
        );

        try {
            $this->environmentFileWriter->mergeKeysIntoEnv(base_path(), [
                HostSystemUpdateVersionWriter::ENV_DISPLAY_VERSION => $display,
                HostSystemUpdateVersionWriter::ENV_UPDATE_VERSION => $update,
            ]);

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

            $envSnapshotRestored = false;

            if ($this->shouldRestoreEnvSnapshot()) {
                $envSnapshotRestored = $this->attemptEnvSnapshotRestore($execution);
            }

            $summary = [
                'display_version' => $display,
                'update_version' => $update,
                'env_snapshot_restored' => $envSnapshotRestored,
            ];

            $this->executionLogger->logStepCompleted(
                $execution,
                HostSystemUpdateExecutionStep::Rollback->value,
                (string) __('dberp.system_update.recovery.log.env_restore_completed'),
                $summary,
                $startedAt,
            );
            $this->auditLogger->logSystemUpdateRollbackEnvRestoreCompleted($execution, $summary);

            return $summary;
        } catch (RuntimeException $exception) {
            $recoveryException = HostSystemUpdateRecoveryException::envRestoreFailed($exception->getMessage());

            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::Rollback->value,
                $recoveryException->userFacingMessage(),
                $recoveryException->errorCode(),
                $recoveryException->userFacingMessage(),
                null,
                $startedAt,
            );
            $this->auditLogger->logSystemUpdateRollbackEnvRestoreFailed($execution, $recoveryException);

            throw $recoveryException;
        }
    }

    private function shouldRestoreEnvSnapshot(): bool
    {
        return filter_var(
            config('dberp.system_update.restore_env_snapshot_on_rollback', false),
            FILTER_VALIDATE_BOOLEAN,
        );
    }

    private function attemptEnvSnapshotRestore(HostSystemUpdateExecution $execution): bool
    {
        $backup = $execution->backups()
            ->where('backup_type', HostSystemUpdateBackup::TYPE_ENV_SNAPSHOT)
            ->where('status', HostSystemUpdateBackup::STATUS_COMPLETED)
            ->latest('id')
            ->first();

        if (! $backup instanceof HostSystemUpdateBackup || ! is_string($backup->backup_path)) {
            return false;
        }

        $snapshotPath = HostSystemUpdateWorkspaceStorage::absoluteFromRelativeStorage($backup->backup_path);

        if (! HostSystemUpdateWorkspaceStorage::isWithinUpdatesWorkspace($snapshotPath)
            || ! is_file($snapshotPath)) {
            return false;
        }

        $expected = strtolower(trim((string) ($backup->checksum ?? '')));
        $actual = hash_file('sha256', $snapshotPath);

        if (($backup->checksum_algorithm ?? 'sha256') !== 'sha256'
            || preg_match('/^[a-f0-9]{64}$/', $expected) !== 1
            || ! is_string($actual)
            || ! hash_equals($expected, strtolower($actual))) {
            return false;
        }

        $target = base_path('.env');

        return copy($snapshotPath, $target);
    }
}
