<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Models\HostSystemUpdateExecution;

/**
 * 将执行任务错误码/失败上下文映射为可操作的恢复建议（中文）。
 */
final class HostSystemUpdateRecoveryGuidanceService
{
    public function __construct(
        private readonly HostSystemUpdateEnvironmentDiagnosticService $environmentDiagnosticService,
    ) {}

    public function resolve(?HostSystemUpdateExecution $execution = null): HostSystemUpdateRecoveryGuidance
    {
        $errorCode = $this->normalizeToken((string) ($execution?->error_code ?? ''));
        $errorMessage = mb_strtolower(trim((string) ($execution?->error_message ?? '')));
        $haystack = trim($errorCode.' '.$errorMessage);

        if ($haystack === '') {
            return $this->fallbackGuidance($execution);
        }

        if ($this->matches($haystack, ['recovery_maintenance', 'maintenance_up', 'maintenance_down', 'maintenance'])) {
            return $this->guidance('maintenance_mode', true);
        }

        if ($this->matches($haystack, ['post_apply_migration', 'migration_failed', 'migration'])) {
            return $this->guidance('migration_failure', true);
        }

        if ($this->matches($haystack, ['checksum', 'signature', 'verified', 'verification'])) {
            return $this->guidance('package_integrity', false);
        }

        if ($this->matches($haystack, ['zip_archive', 'zip extension', 'ziparchive'])) {
            return $this->guidance('zip_archive_unavailable', true);
        }

        if ($this->matches($haystack, ['staging_archive', 'archive_open', 'unsafe_archive', 'extract', 'staging_unsupported', 'package_extraction'])) {
            return $this->guidance('package_extraction', true);
        }

        if ($this->matches($haystack, ['proc_open', 'proc_close', 'proc_get_status', 'proc_terminate', 'process_function'])) {
            return $this->guidance('proc_open_disabled', true);
        }

        if ($this->matches($haystack, ['writable_env', '.env', 'env_restore', 'env_snapshot'])) {
            return $this->guidance('env_not_writable', true);
        }

        if ($this->matches($haystack, ['bootstrap_cache', 'bootstrap/cache'])) {
            return $this->guidance('bootstrap_cache_not_writable', true);
        }

        if ($this->matches($haystack, ['writable_vendor', 'vendor_copy', 'vendor_file', '/vendor'])) {
            return $this->guidance('vendor_not_writable', true);
        }

        if ($this->matches($haystack, ['writable_base', 'writable_app', 'copy_failed', 'file_execution_copy', 'permission denied', 'not writable'])) {
            return $this->guidance('project_not_writable', true);
        }

        if ($this->matches($haystack, ['backup_failed', 'file_restore', 'rollback'])) {
            return $this->guidance('backup_or_rollback', true);
        }

        return $this->fallbackGuidance($execution);
    }

    private function guidance(string $topic, bool $suggestTerminalUpdate): HostSystemUpdateRecoveryGuidance
    {
        /** @var list<string> $steps */
        $steps = (array) trans('dberp.system_update_center.recovery_guidance.topics.'.$topic.'.steps');

        if ($steps === []) {
            return $this->fallbackGuidance(null);
        }

        return new HostSystemUpdateRecoveryGuidance(
            title: (string) __('dberp.system_update_center.recovery_guidance.topics.'.$topic.'.title'),
            steps: array_values(array_filter($steps, static fn (mixed $step): bool => is_string($step) && $step !== '')),
            suggestTerminalUpdate: $suggestTerminalUpdate,
        );
    }

    private function fallbackGuidance(?HostSystemUpdateExecution $execution): HostSystemUpdateRecoveryGuidance
    {
        $report = $this->environmentDiagnosticService->diagnose();
        $suggestTerminal = ! $report->canRunBrowserUpdate || $report->warningCount > 0;

        if (! $report->canRunBrowserUpdate) {
            $blocking = $report->firstBlockingSummary();

            if ($blocking !== null) {
                /** @var list<string> $steps */
                $steps = (array) trans('dberp.system_update_center.recovery_guidance.topics.unknown_failure_with_environment.steps');

                return new HostSystemUpdateRecoveryGuidance(
                    title: (string) __('dberp.system_update_center.recovery_guidance.topics.unknown_failure.title'),
                    steps: array_map(
                        static fn (string $step): string => str_replace(':environment_reason', $blocking, $step),
                        array_values(array_filter($steps, static fn (mixed $step): bool => is_string($step) && $step !== '')),
                    ),
                    suggestTerminalUpdate: true,
                );
            }
        }

        /** @var list<string> $steps */
        $steps = (array) trans('dberp.system_update_center.recovery_guidance.topics.unknown_failure.steps');

        return new HostSystemUpdateRecoveryGuidance(
            title: (string) __('dberp.system_update_center.recovery_guidance.topics.unknown_failure.title'),
            steps: array_values(array_filter($steps, static fn (mixed $step): bool => is_string($step) && $step !== '')),
            suggestTerminalUpdate: $suggestTerminal,
        );
    }

    /**
     * @param  list<string>  $needles
     */
    private function matches(string $haystack, array $needles): bool
    {
        foreach ($needles as $needle) {
            $normalized = $this->normalizeToken($needle);

            if ($normalized !== '' && str_contains($haystack, $normalized)) {
                return true;
            }
        }

        return false;
    }

    private function normalizeToken(string $value): string
    {
        return mb_strtolower(trim(str_replace(['-', ' '], '_', $value)));
    }
}
