<?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\HostSystemUpdateExecutionStatus;
use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Exceptions\SupportCenter\HostSystemUpdateRecoveryException;
use App\Models\HostSystemUpdateExecution;
use App\Models\HostSystemUpdateExecutionLog;
use Illuminate\Support\Facades\File;
use Throwable;

/**
 * 维护模式进入/退出封装（经 {@see HostSystemUpdateMaintenanceCommandHandler}，默认进程内 Artisan::call）。
 */
final class HostSystemUpdateMaintenanceModeService
{
    public function __construct(
        private readonly HostSystemUpdateMaintenanceCommandHandler $commandHandler,
        private readonly HostSystemUpdateExecutionLogger $executionLogger,
        private readonly SupportCenterAuditLogger $auditLogger,
    ) {}

    public function enterMaintenance(HostSystemUpdateExecution $execution): HostSystemUpdateArtisanCommandResult
    {
        $startedAt = now()->utc();
        $this->executionLogger->logStepStarted(
            $execution,
            HostSystemUpdateExecutionStep::MaintenanceDown->value,
            (string) __('dberp.system_update.recovery.log.maintenance_down_started'),
        );

        $result = $this->commandHandler->executeDown(null);

        if (! $result->succeeded()) {
            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::MaintenanceDown->value,
                (string) __('dberp.system_update.recovery.log.maintenance_down_failed'),
                'recovery_maintenance_down_failed',
                (string) __('dberp.system_update.recovery.log.maintenance_down_failed'),
                $result->toLogContext(),
                $startedAt,
            );

            throw HostSystemUpdateRecoveryException::maintenanceCommandFailed('down', $result->exitCode);
        }

        $this->executionLogger->logStepCompleted(
            $execution,
            HostSystemUpdateExecutionStep::MaintenanceDown->value,
            (string) __('dberp.system_update.recovery.log.maintenance_down_completed'),
            $result->toLogContext(),
            $startedAt,
        );

        return $result;
    }

    public function attemptLeaveMaintenance(HostSystemUpdateExecution $execution): HostSystemUpdateArtisanCommandResult
    {
        $startedAt = now()->utc();
        $this->executionLogger->logStepStarted(
            $execution,
            HostSystemUpdateExecutionStep::MaintenanceUp->value,
            (string) __('dberp.system_update.recovery.log.maintenance_up_started'),
        );

        try {
            $result = $this->commandHandler->executeUp(null);

            if ($result->succeeded()) {
                $this->executionLogger->logStepCompleted(
                    $execution,
                    HostSystemUpdateExecutionStep::MaintenanceUp->value,
                    (string) __('dberp.system_update.recovery.log.maintenance_up_completed'),
                    $result->toLogContext(),
                    $startedAt,
                );
                $this->auditLogger->logSystemUpdateMaintenanceModeRestored($execution, $result);

                return $result;
            }

            $failureMessage = (string) __('dberp.system_update.recovery.maintenance_up_failed_detail', [
                'exit_code' => (string) $result->exitCode,
            ]);

            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::MaintenanceUp->value,
                (string) __('dberp.system_update.recovery.log.maintenance_up_failed'),
                'recovery_maintenance_up_failed',
                $failureMessage,
                $result->toLogContext(),
                $startedAt,
            );
            $this->auditLogger->logSystemUpdateMaintenanceModeRestoreFailed($execution, $result);

            throw HostSystemUpdateRecoveryException::maintenanceCommandFailed('up', $result->exitCode);
        } catch (HostSystemUpdateRecoveryException $exception) {
            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::MaintenanceUp->value,
                $exception->userFacingMessage(),
                $exception->errorCode(),
                $exception->userFacingMessage(),
                null,
                $startedAt,
            );
            $this->auditLogger->logSystemUpdateMaintenanceModeRestoreFailed($execution, null, $exception->errorCode());

            throw $exception;
        }
    }

    /**
     * 失败/中断路径：尽力退出维护，不抛异常，避免阻断失败结果回传 UI。
     */
    public function attemptLeaveMaintenanceOnFailure(?HostSystemUpdateExecution $execution): void
    {
        if ($execution instanceof HostSystemUpdateExecution
            && $this->shouldRemainInMaintenanceForRecovery($execution->fresh() ?? $execution)) {
            return;
        }

        if ($execution === null && ! $this->maintenanceArtifactsExist()) {
            return;
        }

        try {
            if ($execution instanceof HostSystemUpdateExecution && $this->executionHasEnteredMaintenance($execution)) {
                $this->attemptLeaveMaintenance($execution);
            } else {
                $this->commandHandler->executeUp(null);
            }
        } catch (Throwable $throwable) {
            report($throwable);
            $this->recoverMaintenanceArtifactsAfterFailedCommand($execution);
        }
    }

    public function executionHasEnteredMaintenance(HostSystemUpdateExecution $execution): bool
    {
        return $execution->logs()
            ->where('step', HostSystemUpdateExecutionStep::MaintenanceDown->value)
            ->where('status', HostSystemUpdateExecutionLog::STATUS_COMPLETED)
            ->exists();
    }

    public function shouldRemainInMaintenanceForRecovery(?HostSystemUpdateExecution $execution): bool
    {
        return $execution instanceof HostSystemUpdateExecution
            && in_array((string) ($execution->status ?? ''), [
                HostSystemUpdateExecutionStatus::RollbackRequired->value,
                HostSystemUpdateExecutionStatus::ManualRecoveryRequired->value,
                HostSystemUpdateExecutionStatus::RollingBack->value,
                HostSystemUpdateExecutionStatus::RollbackFailed->value,
            ], true);
    }

    private function maintenanceArtifactsExist(): bool
    {
        return is_file(storage_path('framework/down'))
            || is_file(storage_path('framework/maintenance.php'));
    }

    /**
     * WARNING: 仅在 artisan up 失败时作为系统更新收尾兜底，直接移除 Laravel 维护文件。
     */
    private function recoverMaintenanceArtifactsAfterFailedCommand(?HostSystemUpdateExecution $execution): void
    {
        $paths = [
            storage_path('framework/down'),
            storage_path('framework/maintenance.php'),
        ];

        foreach ($paths as $path) {
            if (is_file($path)) {
                File::delete($path);
            }
        }

        if ($execution instanceof HostSystemUpdateExecution) {
            $this->executionLogger->logStepFailed(
                $execution,
                HostSystemUpdateExecutionStep::MaintenanceUp->value,
                (string) __('dberp.system_update.recovery.log.maintenance_up_failed'),
                'recovery_maintenance_up_file_fallback',
                (string) __('dberp.system_update.recovery.maintenance_up_file_fallback_detail'),
                ['fallback' => 'delete_framework_maintenance_files'],
                now()->utc(),
            );
        }
    }
}
