<?php

namespace App\Http\Controllers\Install;

use App\Http\Controllers\Controller;
use App\Support\Installer;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use PDO;
use RuntimeException;
use Throwable;

class InstallerController extends Controller
{
    public function show()
    {
        return view('install.index', [
            'checks' => Installer::environmentChecks(),
            'defaults' => [
                'app_name' => env('APP_NAME', 'ChatSell OS'),
                'app_url' => request()->getSchemeAndHttpHost(),
                'db_connection' => 'mysql',
                'db_host' => '127.0.0.1',
                'db_port' => '3306',
                'db_database' => '',
                'db_username' => '',
                'db_password' => '',
                'superadmin_email' => env('SUPERADMIN_EMAIL', 'superadmin@chatsell.local'),
                'superadmin_password' => env('SUPERADMIN_PASSWORD', 'SuperAdmin123!'),
            ],
        ]);
    }

    public function install(Request $request): RedirectResponse
    {
        if (! Installer::passesEnvironmentChecks()) {
            return back()->withErrors([
                'install' => 'Server requirements are not fully satisfied. Fix them first.',
            ])->withInput();
        }

        $data = $request->validate([
            'app_name' => ['required', 'string', 'max:80'],
            'app_url' => ['required', 'url', 'max:255'],
            'db_connection' => ['required', 'in:mysql,pgsql'],
            'db_host' => ['required', 'string', 'max:255'],
            'db_port' => ['required', 'integer', 'min:1', 'max:65535'],
            'db_database' => ['required', 'string', 'max:255'],
            'db_username' => ['required', 'string', 'max:255'],
            'db_password' => ['nullable', 'string', 'max:255'],
            'superadmin_email' => ['required', 'email', 'max:255'],
            'superadmin_password' => ['required', 'string', 'min:8', 'max:255'],
        ]);

        try {
            $this->assertDatabaseConnection($data);
            $this->writeEnvironmentFile($data);
            $this->applyRuntimeConfig($data);
            $this->runSetupCommands();

            Installer::markInstalled();

            return redirect()
                ->route('login')
                ->with('status', 'Installation completed. Sign in with your super admin credentials.')
                ->with('installer_credentials', [
                    'email' => strtolower($data['superadmin_email']),
                    'password' => $data['superadmin_password'],
                ]);
        } catch (Throwable $e) {
            return back()->withErrors([
                'install' => 'Installation failed: '.$e->getMessage(),
            ])->withInput();
        }
    }

    /**
     * @param array<string, mixed> $data
     */
    private function assertDatabaseConnection(array $data): void
    {
        $driver = (string) $data['db_connection'];
        $host = (string) $data['db_host'];
        $port = (string) $data['db_port'];
        $database = (string) $data['db_database'];
        $username = (string) $data['db_username'];
        $password = (string) ($data['db_password'] ?? '');

        $dsn = $driver === 'pgsql'
            ? "pgsql:host={$host};port={$port};dbname={$database}"
            : "mysql:host={$host};port={$port};dbname={$database}";

        new PDO($dsn, $username, $password, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_TIMEOUT => 8,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    private function writeEnvironmentFile(array $data): void
    {
        $envPath = base_path('.env');
        $templatePath = base_path('.env.example');

        $contents = file_exists($envPath)
            ? (string) file_get_contents($envPath)
            : (string) file_get_contents($templatePath);

        if ($contents === '') {
            throw new RuntimeException('Could not read .env template.');
        }

        $appKey = 'base64:'.base64_encode(random_bytes(32));
        $superAdminEmail = strtolower((string) $data['superadmin_email']);
        $superAdminPassword = (string) $data['superadmin_password'];

        $updates = [
            'APP_NAME' => (string) $data['app_name'],
            'APP_ENV' => 'production',
            'APP_KEY' => $appKey,
            'APP_DEBUG' => 'false',
            'APP_URL' => (string) $data['app_url'],
            'APP_INSTALLED' => 'true',
            'DB_CONNECTION' => (string) $data['db_connection'],
            'DB_HOST' => (string) $data['db_host'],
            'DB_PORT' => (string) $data['db_port'],
            'DB_DATABASE' => (string) $data['db_database'],
            'DB_USERNAME' => (string) $data['db_username'],
            'DB_PASSWORD' => (string) ($data['db_password'] ?? ''),
            'CACHE_DRIVER' => 'file',
            'SESSION_DRIVER' => 'file',
            'QUEUE_CONNECTION' => 'sync',
            'BROADCAST_DRIVER' => 'log',
            'OPERATOR_EMAILS' => $superAdminEmail,
            'SUPERADMIN_EMAIL' => $superAdminEmail,
            'SUPERADMIN_PASSWORD' => $superAdminPassword,
        ];

        foreach ($updates as $key => $value) {
            $contents = $this->replaceEnvValue($contents, $key, $value);
            putenv("{$key}={$value}");
            $_ENV[$key] = $value;
            $_SERVER[$key] = $value;
        }

        if (file_put_contents($envPath, $contents) === false) {
            throw new RuntimeException('Could not write .env file.');
        }
    }

    private function replaceEnvValue(string $contents, string $key, string $value): string
    {
        $escapedValue = $this->escapeEnvValue($value);
        $pattern = "/^{$key}=.*$/m";

        if (preg_match($pattern, $contents)) {
            return preg_replace($pattern, "{$key}={$escapedValue}", $contents) ?: $contents;
        }

        return rtrim($contents).PHP_EOL."{$key}={$escapedValue}".PHP_EOL;
    }

    private function escapeEnvValue(string $value): string
    {
        if ($value === '') {
            return '';
        }

        if (preg_match('/\s|#|=|"/', $value)) {
            return '"'.str_replace('"', '\"', $value).'"';
        }

        return $value;
    }

    /**
     * @param array<string, mixed> $data
     */
    private function applyRuntimeConfig(array $data): void
    {
        config([
            'app.key' => env('APP_KEY'),
            'database.default' => (string) $data['db_connection'],
            'database.connections.'.(string) $data['db_connection'] => array_merge(
                config('database.connections.'.(string) $data['db_connection'], []),
                [
                    'host' => (string) $data['db_host'],
                    'port' => (string) $data['db_port'],
                    'database' => (string) $data['db_database'],
                    'username' => (string) $data['db_username'],
                    'password' => (string) ($data['db_password'] ?? ''),
                ]
            ),
        ]);

        DB::purge((string) $data['db_connection']);
        DB::reconnect((string) $data['db_connection']);
    }

    private function runSetupCommands(): void
    {
        Artisan::call('optimize:clear');
        Artisan::call('migrate', ['--force' => true]);
        Artisan::call('db:seed', ['--force' => true]);
        Artisan::call('chatsell:ensure-operator');
    }
}
