<?php

declare(strict_types=1);

namespace App\Http\Controllers\Install;

use App\Http\Controllers\Controller;
use App\Http\Requests\Install\InstallInitializeRequest;
use App\Services\Install\DBErpInstallStatus;
use App\Services\Install\InstallerDatabaseStateChecker;
use App\Services\Install\InstallWizardTimezoneOptions;
use App\Services\Install\WebInstallDatabaseSituation;
use App\Services\Install\WebInstallPendingPayloadStore;
use App\Services\Install\WebInstallProgressSessionManager;
use App\Services\Install\WebInstallProgressStatus;
use App\Services\Install\WebInstallUserMessages;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;
use RuntimeException;

final class InstallInitializeController extends Controller
{
    public function show(
        Request $request,
        InstallerDatabaseStateChecker $installerDatabaseStateChecker,
        DBErpInstallStatus $dberpInstallStatus,
    ): View {
        $databaseState = $installerDatabaseStateChecker->inspectCurrentConnection();
        $applicationMarkedInstalled = $dberpInstallStatus->isInstalled();
        $situation = $installerDatabaseStateChecker->classifyWizardDatabaseState($applicationMarkedInstalled);
        $databaseStatusMessage = WebInstallUserMessages::initializePageDatabaseMessage(
            $situation,
            $applicationMarkedInstalled,
            $databaseState,
        );

        return view('install.initialize', [
            'defaults' => [
                'system_url' => old('system_url', rtrim($request->root(), '/')),
                'timezone' => old('timezone', 'Asia/Shanghai'),
                'admin_name' => old('admin_name', ''),
                'login_account' => old('login_account', ''),
                'admin_email' => old('admin_email', ''),
            ],
            'timezoneOptions' => InstallWizardTimezoneOptions::sortedForSelect(),
            'databaseState' => $databaseState,
            'databaseSituation' => $situation,
            'databaseStatusMessage' => $databaseStatusMessage,
            'applicationMarkedInstalled' => $applicationMarkedInstalled,
            'canAttemptInstall' => $this->canAttemptInstall($situation, $databaseState, $applicationMarkedInstalled),
        ]);
    }

    public function store(
        InstallInitializeRequest $request,
        InstallerDatabaseStateChecker $installerDatabaseStateChecker,
        DBErpInstallStatus $dberpInstallStatus,
        WebInstallProgressSessionManager $progressSessionManager,
        WebInstallPendingPayloadStore $pendingPayloadStore,
    ): RedirectResponse {
        $existingTask = $progressSessionManager->resolveActiveTask();
        if ($existingTask !== null) {
            if (in_array($existingTask->status, [
                WebInstallProgressStatus::Completed,
                WebInstallProgressStatus::Warning,
            ], true) && $dberpInstallStatus->isInstalled()) {
                return redirect()->route('install.finished');
            }

            return redirect()->route('install.progress', ['install_id' => $existingTask->installId]);
        }

        $databaseState = $installerDatabaseStateChecker->inspectCurrentConnection();
        $applicationMarkedInstalled = $dberpInstallStatus->isInstalled();
        $situation = $installerDatabaseStateChecker->classifyWizardDatabaseState($applicationMarkedInstalled);
        $reinstallReset = $request->boolean('reinstall_reset');

        if ($applicationMarkedInstalled) {
            return redirect()->route('install.finished');
        }

        if ($situation === WebInstallDatabaseSituation::PartialInstallation && ! $reinstallReset) {
            return redirect()
                ->route('install.initialize')
                ->withInput($request->except(['password', 'password_confirmation']))
                ->withErrors(['install' => WebInstallUserMessages::PARTIAL_INSTALLATION_ON_SUBMIT]);
        }

        if (! $this->canAttemptInstall($situation, $databaseState, $applicationMarkedInstalled) && ! $reinstallReset) {
            return redirect()
                ->route('install.initialize')
                ->withInput($request->except(['password', 'password_confirmation']))
                ->withErrors(['install' => WebInstallUserMessages::notInstallableInitializeMessage($databaseState)]);
        }

        if ($situation === WebInstallDatabaseSituation::NotInstallable && $reinstallReset) {
            if (! ($databaseState['checks']['reinstall_reset_supported'] ?? false)) {
                return redirect()
                    ->route('install.initialize')
                    ->withInput($request->except(['password', 'password_confirmation']))
                    ->withErrors([
                        'install' => '当前数据库中存在与本系统无关的其他数据表或视图，无法由安装向导自动清理。请更换专用空库或联系技术人员处理。',
                    ]);
            }
        }

        $payload = $request->validated();
        unset($payload['reinstall_reset'], $payload['password_confirmation']);
        $payload['reinstall_reset'] = $reinstallReset;

        try {
            $progress = $progressSessionManager->begin();
            $pendingPayloadStore->store($progress->installId, $payload);
        } catch (InvalidArgumentException) {
            return redirect()
                ->route('install.initialize')
                ->withInput($request->except(['password', 'password_confirmation']))
                ->withErrors(['install' => '无法创建安装任务，请检查 storage/framework 目录写入权限后重试。']);
        } catch (RuntimeException) {
            return redirect()
                ->route('install.initialize')
                ->withInput($request->except(['password', 'password_confirmation']))
                ->withErrors(['install' => '无法创建安装任务，请检查 storage/framework 目录写入权限后重试。']);
        }

        return redirect()->route('install.progress', ['install_id' => $progress->installId]);
    }

    /**
     * @param  array{
     *     safe: bool,
     *     checks: array{reinstall_reset_supported?: bool}
     * }  $databaseState
     */
    private function canAttemptInstall(
        WebInstallDatabaseSituation $situation,
        array $databaseState,
        bool $applicationMarkedInstalled,
    ): bool {
        if ($applicationMarkedInstalled) {
            return false;
        }

        if ($situation === WebInstallDatabaseSituation::EmptySafe
            || $situation === WebInstallDatabaseSituation::EmptyMigrationsTableOnly) {
            return true;
        }

        if ($situation === WebInstallDatabaseSituation::PartialInstallation) {
            return (bool) ($databaseState['checks']['reinstall_reset_supported'] ?? false);
        }

        return (bool) ($databaseState['checks']['reinstall_reset_supported'] ?? false);
    }
}
