<?php

namespace App\Http\Controllers\Install;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Support\Installation;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
use Throwable;

/**
 * The one-time web installer for a cPanel deployment.
 *
 * It runs before there is a database, so it renders plain Blade and touches
 * nothing that needs a session store or the compiled front end. Once it locks
 * itself, every route here answers 404.
 */
class InstallerController extends Controller
{
    public function show(): View|RedirectResponse
    {
        if (Installation::isInstalled()) {
            return redirect('/');
        }

        return view('install.index', [
            'requirements' => Installation::requirements(),
            'ready' => Installation::requirementsMet(),
            'appUrl' => request()->getSchemeAndHttpHost(),
        ]);
    }

    public function store(Request $request): RedirectResponse|View
    {
        if (Installation::isInstalled()) {
            return redirect('/');
        }

        $validated = $request->validate([
            'app_name' => ['required', 'string', 'max:60'],
            'app_url' => ['required', 'url', 'max:180'],
            'db_host' => ['required', 'string', 'max:120'],
            'db_port' => ['required', 'integer', 'min:1', 'max:65535'],
            'db_database' => ['required', 'string', 'max:64'],
            'db_username' => ['required', 'string', 'max:64'],
            'db_password' => ['nullable', 'string', 'max:190'],
            'admin_name' => ['required', 'string', 'max:120'],
            'admin_email' => ['required', 'email', 'max:180'],
            'admin_password' => ['required', 'confirmed', Password::min(8)],
        ], [
            'admin_password.confirmed' => 'Las contraseñas no coinciden.',
        ]);

        $connection = [
            'driver' => 'mysql',
            'host' => $validated['db_host'],
            'port' => (int) $validated['db_port'],
            'database' => $validated['db_database'],
            'username' => $validated['db_username'],
            'password' => $validated['db_password'] ?? '',
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
        ];

        try {
            Config::set('database.connections.installer', $connection);
            DB::purge('installer');
            DB::connection('installer')->getPdo();
        } catch (Throwable $exception) {
            return back()
                ->withInput($request->except(['db_password', 'admin_password', 'admin_password_confirmation']))
                ->withErrors(['db_host' => 'No fue posible conectar: '.$exception->getMessage()]);
        }

        Installation::writeEnv([
            'APP_NAME' => $validated['app_name'],
            'APP_ENV' => 'production',
            'APP_KEY' => Installation::generateAppKey(),
            'APP_DEBUG' => 'false',
            'APP_URL' => rtrim($validated['app_url'], '/'),
            'DB_CONNECTION' => 'mysql',
            'DB_HOST' => $validated['db_host'],
            'DB_PORT' => (string) $validated['db_port'],
            'DB_DATABASE' => $validated['db_database'],
            'DB_USERNAME' => $validated['db_username'],
            'DB_PASSWORD' => $validated['db_password'] ?? '',
            'SESSION_DRIVER' => 'database',
            'CACHE_STORE' => 'database',
            'QUEUE_CONNECTION' => 'sync',
        ]);

        // The rest of this request must speak to the database it just configured.
        Config::set('database.default', 'mysql');
        Config::set('database.connections.mysql', $connection);
        DB::purge('mysql');

        try {
            Artisan::call('migrate', ['--force' => true]);

            User::query()->updateOrCreate(
                ['email' => $validated['admin_email']],
                [
                    'name' => $validated['admin_name'],
                    'password' => Hash::make($validated['admin_password']),
                    'role' => 'admin',
                    'email_verified_at' => now(),
                ],
            );
        } catch (Throwable $exception) {
            return back()
                ->withInput($request->except(['db_password', 'admin_password', 'admin_password_confirmation']))
                ->withErrors(['db_database' => 'La instalación falló: '.$exception->getMessage()]);
        }

        Installation::lock(config('app.version', '1.0.0'));
        $this->clearCaches();

        return redirect('/instalar/listo');
    }

    public function done(): View|RedirectResponse
    {
        if (! Installation::isInstalled()) {
            return redirect('/instalar');
        }

        return view('install.done');
    }

    private function clearCaches(): void
    {
        foreach (['config:clear', 'route:clear', 'view:clear'] as $command) {
            try {
                Artisan::call($command);
            } catch (Throwable) {
                // A cold install has nothing cached; failing here is harmless.
            }
        }
    }
}
