<?php

declare(strict_types=1);

namespace App\Services\Install;

use DomainException;
use Illuminate\Database\QueryException;
use PDOException;
use RuntimeException;
use Throwable;

final class WebInstallUserMessages
{
    public const EMPTY_DATABASE_READY = '当前数据库可用于安装。';

    public const APPLICATION_ALREADY_INSTALLED = 'DBErp 已完成安装。';

    public const DATABASE_NOT_INSTALLABLE_BEFORE_MIGRATION = '当前数据库暂不适合安装：数据库状态不满足安装条件。';

    public const INCOMPLETE_DATABASE_RECORDS = '数据库中已有本系统相关结构，但安装信息不完整，无法确认能否安全继续。请更换空数据库或联系技术支持。';

    public const FINALIZATION_INCOMPLETE = '基础数据已创建，但安装尚未全部完成。请检查服务器目录写入权限后重试，或联系技术支持。';

    public const STORAGE_LINK_PERMISSION_FAILED = '因目录权限或服务器限制，未能自动配置公共文件访问路径。请联系服务器管理员处理后再使用上传等功能。';

    public const STORAGE_LINK_INVALID_EXISTING_PATH = '站点公共目录下存在无效的文件访问配置，安装程序未自动覆盖。请联系服务器管理员清理后重试。';

    public const STORAGE_LINK_PUBLIC_NOT_WRITABLE = '站点公共目录不可写，未能自动配置文件访问路径。请调整目录权限后重试，或联系服务器管理员。';

    public const INSTALL_COMPLETED_WITH_FINALIZATION_WARNINGS = '安装已完成，但个别收尾步骤未成功（例如公共文件访问或缓存刷新）。一般不影响登录，可按提示补做或联系技术支持。';

    public const PARTIAL_INSTALLATION_ON_VISIT = '检测到上次安装未完全结束。为避免覆盖已有数据，安装向导已暂停，请先处理未完成项或联系技术支持。';

    public const PARTIAL_INSTALLATION_ON_SUBMIT = '检测到数据库中已有本系统结构，本次提交不会重复建库。仅测试环境可勾选「重新安装」清空后重装；生产环境请更换空库或联系技术支持。';

    public const MIGRATION_FAILED = '数据表创建失败，请检查数据库账号权限、数据库版本与字符集（建议 utf8mb4）后重试。';

    public const WRITING_ENV_FAILED = '系统配置保存失败，请检查程序目录写入权限后重试。';

    public const CREATING_ADMIN_FAILED = '超级管理员创建失败，请检查数据库连接与账号、密码后重试。';

    public const ADMIN_BOOTSTRAP_TABLES_MISSING = '创建管理员账号所需的数据尚未就绪。请确认「创建数据表」步骤已成功，或重新安装。';

    public const MIGRATION_INCOMPLETE_TABLES = '数据表未全部创建成功。请检查数据库权限，或在仅用于测试的空库上重新安装。';

    public const UPDATING_SYSTEM_PROFILE_FAILED = '系统档案初始化失败，请检查数据库权限后重试。';

    public const WRITING_INSTALL_MARKERS_FAILED = '安装状态保存失败，请检查程序目录写入权限后重试。';

    public const OPTIONAL_FINALIZERS_FAILED = '收尾检查未全部完成，请查看安装日志并按提示处理。';

    /**
     * @param  array{
     *     safe: bool,
     *     summary: string,
     *     details: list<string>,
     *     checks: array{
     *         core_tables_exist?: bool,
     *         erp_migration_record_count?: int,
     *     }
     * }  $databaseState
     */
    public static function initializePageDatabaseMessage(
        WebInstallDatabaseSituation $situation,
        bool $applicationMarkedInstalled,
        array $databaseState,
    ): string {
        if ($applicationMarkedInstalled) {
            return self::APPLICATION_ALREADY_INSTALLED;
        }

        return match ($situation) {
            WebInstallDatabaseSituation::EmptySafe,
            WebInstallDatabaseSituation::EmptyMigrationsTableOnly => self::EMPTY_DATABASE_READY,
            WebInstallDatabaseSituation::PartialInstallation => self::PARTIAL_INSTALLATION_ON_VISIT,
            WebInstallDatabaseSituation::NotInstallable => self::notInstallableInitializeMessage($databaseState),
        };
    }

    /**
     * @param  array{
     *     details: list<string>,
     *     checks: array{
     *         core_tables_exist?: bool,
     *         erp_migration_record_count?: int,
     *     }
     * }  $databaseState
     */
    public static function notInstallableInitializeMessage(array $databaseState): string
    {
        $checks = $databaseState['checks'];

        if (($checks['core_tables_exist'] ?? false) && ($checks['erp_migration_record_count'] ?? 0) === 0) {
            return self::INCOMPLETE_DATABASE_RECORDS;
        }

        return self::databaseNotInstallableDetail($databaseState);
    }

    /**
     * @param  array{summary: string, details: list<string>}  $databaseState
     */
    public static function databaseNotInstallableDetail(array $databaseState): string
    {
        if (isset($databaseState['details'][0]) && is_string($databaseState['details'][0]) && trim($databaseState['details'][0]) !== '') {
            return trim($databaseState['details'][0]);
        }

        return self::DATABASE_NOT_INSTALLABLE_BEFORE_MIGRATION;
    }

    public static function failureMessageForPhase(WebInstallPhase $phase, Throwable $exception): string
    {
        if ($exception instanceof DatabaseNotInstallableException
            || $exception instanceof WebInstallFinalizationIncompleteException
            || $exception instanceof DomainException) {
            return self::sanitizeFailureDetail($exception->getMessage(), $phase);
        }

        if ($exception instanceof QueryException || $exception instanceof PDOException) {
            return self::defaultFailureForPhase($phase);
        }

        if ($exception instanceof RuntimeException) {
            $message = trim($exception->getMessage());
            if ($message !== ''
                && ! self::isTechnicalFailureMessage($message)
                && ! self::containsInternalVersionLeak($message)) {
                return $message;
            }
        }

        return self::defaultFailureForPhase($phase);
    }

    public static function sanitizeFailureDetail(string $message, ?WebInstallPhase $phase = null): string
    {
        $message = trim($message);
        if ($message === '') {
            return $phase !== null
                ? self::defaultFailureForPhase($phase)
                : '安装过程中发生错误，请稍后重试。';
        }

        if (self::isTechnicalFailureMessage($message) || self::containsInternalVersionLeak($message)) {
            return $phase !== null
                ? self::defaultFailureForPhase($phase)
                : '安装过程中发生错误，请稍后重试。';
        }

        return $message;
    }

    public static function containsInternalVersionLeak(string $message): bool
    {
        $normalized = strtolower($message);

        if (str_contains($message, '内部更新号')
            || str_contains($normalized, 'dberp_update_version')
            || str_contains($normalized, 'update_version')
            || str_contains($normalized, 'from_update_version')
            || str_contains($normalized, 'to_update_version')) {
            return true;
        }

        $internalUpdate = (string) config('dberp.version.update_version', '');
        if ($internalUpdate !== '' && preg_match('/\b'.preg_quote($internalUpdate, '/').'\b/', $message) === 1) {
            return true;
        }

        $build = config('dberp.version.build');
        if (is_string($build) && $build !== '' && str_contains($message, $build)) {
            return true;
        }

        return false;
    }

    public static function isTechnicalFailureMessage(string $message): bool
    {
        $normalized = strtolower($message);

        return str_contains($message, 'SQLSTATE')
            || str_contains($message, 'Connection:')
            || str_contains($normalized, ' sql: ')
            || str_contains($message, 'Base table or view not found')
            || str_contains($message, 'PDOException')
            || str_contains($message, 'Illuminate\\Database\\')
            || str_contains($message, 'doesn\'t exist')
            || str_contains($message, '.env')
            || str_contains($normalized, 'artisan ')
            || str_contains($message, 'storage/')
            || str_contains($message, 'public/storage');
    }

    public static function defaultFailureForPhase(WebInstallPhase $phase): string
    {
        return match ($phase) {
            WebInstallPhase::WritingEnv => self::WRITING_ENV_FAILED,
            WebInstallPhase::Migrating => self::MIGRATION_FAILED,
            WebInstallPhase::CreatingAdmin => self::CREATING_ADMIN_FAILED,
            WebInstallPhase::UpdatingSystemProfile => self::UPDATING_SYSTEM_PROFILE_FAILED,
            WebInstallPhase::WritingInstallMarkers => self::WRITING_INSTALL_MARKERS_FAILED,
            WebInstallPhase::RunningOptionalFinalizers => self::OPTIONAL_FINALIZERS_FAILED,
            default => '安装过程中发生错误，请稍后重试。',
        };
    }
}
