<?php
/**
 * (c) ININOUT & PYXIS. All Rights Reserved.
 * Wszelkie modyfikacje tego pliku wymagaja zgloszenia oraz zgody autorow.
 * Any modifications to this file require notification and consent of the authors.
 */
/**
 * i-BOK 5 - Instalator
 *
 * Konfiguracja bazy danych + sprawdzenie uprawnień.
 * Dostępny tylko gdy cfg/database.cfg nie ma poprawnego połączenia.
 * Rekonfiguracja (?force) wymaga zalogowanego admina.
 */
error_reporting(0);
date_default_timezone_set('Europe/Warsaw');

define('IBOK_ROOT', __DIR__);
define('IBOK_CFG_DIR', IBOK_ROOT . '/cfg');
define('IBOK_LOGS_DIR', IBOK_ROOT . '/logs');

$db_cfg_file = IBOK_CFG_DIR . '/database.cfg';

// ─── Minimalna sesja (CSRF) ───
if (session_status() === PHP_SESSION_NONE) {
    session_name('IBOK_INSTALL');
    session_start();
}
if (empty($_SESSION['_csrf'])) {
    $_SESSION['_csrf'] = bin2hex(random_bytes(32));
}

// ─── Język ───
$supported_langs = ['pl', 'en'];
if (isset($_GET['lang']) && in_array($_GET['lang'], $supported_langs)) {
    $_SESSION['install_lang'] = $_GET['lang'];
    setcookie('ibok_lang', $_GET['lang'], time() + 86400 * 365, '/');
}
$lang = $_SESSION['install_lang']
    ?? $_COOKIE['ibok_lang']
    ?? (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && strpos($_SERVER['HTTP_ACCEPT_LANGUAGE'], 'pl') !== false ? 'pl' : 'en');
if (!in_array($lang, $supported_langs)) $lang = 'pl';

$T = [
    'pl' => [
        'title'             => 'i-BOK 5 - Instalator',
        'subtitle'          => 'Instalator - konfiguracja bazy danych',
        'step_requirements' => 'Wymagania',
        'step_database'     => 'Baza danych',
        'step_done'         => 'Gotowe',
        'section_requirements' => 'Wymagania systemowe',
        'group_php'         => 'PHP i rozszerzenia',
        'group_dirs'        => 'Katalogi i pliki',
        'group_htaccess'    => 'Zabezpieczenia HTTP',
        'need_required'     => 'wymagane',
        'need_rw'           => 'odczyt + zapis',
        'need_write'        => 'zapis',
        'need_create'       => 'utworzenie pliku',
        'status_ok'         => 'OK',
        'status_missing'    => 'brak',
        'status_no_dir'     => 'nie istnieje',
        'status_no_write'   => 'brak zapisu',
        'status_can_create' => 'możliwe',
        'status_no_perms'   => 'brak uprawnień',
        'htaccess_present'  => 'chroniony',
        'htaccess_missing'  => 'BRAK .htaccess!',
        'htaccess_need'     => '.htaccess Deny',
        'critical_title'    => 'Krytyczne problemy',
        'critical_desc'     => 'Napraw powyższe błędy przed kontynuacją. Upewnij się, że serwer WWW (apache/nginx) ma prawa zapisu do katalogów <code>cfg/</code> i <code>logs/</code>.',
        'section_database'  => 'Połączenie z bazą danych',
        'field_host'        => 'Host',
        'field_port'        => 'Port',
        'field_database'    => 'Nazwa bazy',
        'field_username'    => 'Użytkownik',
        'field_password'    => 'Hasło',
        'field_charset'     => 'Charset',
        'ph_host'           => 'localhost lub IP',
        'ph_database'       => 'np. PYXIS4SQL',
        'ph_username'       => 'login MySQL',
        'ph_password'       => 'hasło MySQL',
        'btn_test'          => 'Testuj połączenie',
        'btn_save'          => 'Zapisz konfigurację',
        'eula_accept'       => 'Akceptuję warunki',
        'eula_link'         => 'umowy licencyjnej (EULA)',
        'eula_required'     => 'Musisz zaakceptować umowę licencyjną.',
        'success_title'     => 'Konfiguracja zapisana!',
        'success_desc'      => 'i-BOK 5 jest gotowy do pracy.',
        'success_btn'       => 'Przejdź do panelu',
        'err_fill_required' => 'Wypełnij host, nazwę bazy i użytkownika.',
        'err_fill_all'      => 'Wypełnij wszystkie wymagane pola.',
        'err_comm'          => 'Błąd komunikacji z serwerem.',
        'err_access_denied' => 'Odmowa dostępu - sprawdź login i hasło.',
        'err_unknown_db'    => 'Baza danych nie istnieje.',
        'err_conn_refused'  => 'Nie można połączyć z serwerem - sprawdź host i port.',
        'err_unknown_host'  => 'Nieznany host - sprawdź adres serwera.',
        'err_conn_fail'     => 'Połączenie nie działa: ',
        'err_save_fail'     => 'Nie można zapisać pliku konfiguracji. Sprawdź uprawnienia katalogu cfg/',
        'err_csrf'          => 'Błąd weryfikacji formularza. Odśwież stronę.',
        'err_eula'          => 'Musisz zaakceptować umowę licencyjną.',
        'err_unknown'       => 'Nieznana akcja.',
        'test_ok'           => 'Połączenie udane!',
        'test_server'       => 'Serwer',
        'test_tables_found' => 'Znalezione tabele',
        'test_no_tables'    => 'Nie znaleziono tabel PYXIS. Sprawdź czy baza jest poprawna.',
        'test_version_warn' => 'Wersja serwera jest za stara. Wymagana: MySQL >= 5.7 lub MariaDB >= 10.2',
        'test_db_charset'   => 'Charset bazy',
        'test_charset_mismatch' => 'Charset bazy (:db) nie pasuje do wybranego (:conn). Polskie znaki mogą się wyświetlać nieprawidłowo. Zmień charset na :db lub pozostaw jeśli wiesz co robisz.',
        'test_charset_auto' => 'Wykryto charset bazy: :charset. Ustawiono automatycznie.',
        'save_ok'           => 'Konfiguracja zapisana pomyślnie!',
        'force_403'         => 'Rekonfiguracja wymaga zalogowanego administratora.',
    ],
    'en' => [
        'title'             => 'i-BOK 5 - Installer',
        'subtitle'          => 'Installer - database configuration',
        'step_requirements' => 'Requirements',
        'step_database'     => 'Database',
        'step_done'         => 'Done',
        'section_requirements' => 'System requirements',
        'group_php'         => 'PHP & extensions',
        'group_dirs'        => 'Directories & files',
        'group_htaccess'    => 'HTTP security',
        'need_required'     => 'required',
        'need_rw'           => 'read + write',
        'need_write'        => 'write',
        'need_create'       => 'create file',
        'status_ok'         => 'OK',
        'status_missing'    => 'missing',
        'status_no_dir'     => 'not found',
        'status_no_write'   => 'not writable',
        'status_can_create' => 'possible',
        'status_no_perms'   => 'no permissions',
        'htaccess_present'  => 'protected',
        'htaccess_missing'  => 'NO .htaccess!',
        'htaccess_need'     => '.htaccess Deny',
        'critical_title'    => 'Critical issues',
        'critical_desc'     => 'Fix the above errors before proceeding. Make sure the web server (apache/nginx) has write permissions to <code>cfg/</code> and <code>logs/</code>.',
        'section_database'  => 'Database connection',
        'field_host'        => 'Host',
        'field_port'        => 'Port',
        'field_database'    => 'Database name',
        'field_username'    => 'Username',
        'field_password'    => 'Password',
        'field_charset'     => 'Charset',
        'ph_host'           => 'localhost or IP',
        'ph_database'       => 'e.g. PYXIS4SQL',
        'ph_username'       => 'MySQL login',
        'ph_password'       => 'MySQL password',
        'btn_test'          => 'Test connection',
        'btn_save'          => 'Save configuration',
        'eula_accept'       => 'I accept the',
        'eula_link'         => 'license agreement (EULA)',
        'eula_required'     => 'You must accept the license agreement.',
        'success_title'     => 'Configuration saved!',
        'success_desc'      => 'i-BOK 5 is ready to use.',
        'success_btn'       => 'Go to panel',
        'err_fill_required' => 'Fill in host, database name and username.',
        'err_fill_all'      => 'Fill in all required fields.',
        'err_comm'          => 'Server communication error.',
        'err_access_denied' => 'Access denied - check username and password.',
        'err_unknown_db'    => 'Database does not exist.',
        'err_conn_refused'  => 'Cannot connect to server - check host and port.',
        'err_unknown_host'  => 'Unknown host - check server address.',
        'err_conn_fail'     => 'Connection failed: ',
        'err_save_fail'     => 'Cannot save configuration file. Check cfg/ directory permissions.',
        'err_csrf'          => 'Form verification error. Please refresh the page.',
        'err_eula'          => 'You must accept the license agreement.',
        'err_unknown'       => 'Unknown action.',
        'test_ok'           => 'Connection successful!',
        'test_server'       => 'Server',
        'test_tables_found' => 'Tables found',
        'test_no_tables'    => 'No PYXIS tables found. Check if database is correct.',
        'test_version_warn' => 'Server version is too old. Required: MySQL >= 5.7 or MariaDB >= 10.2',
        'test_db_charset'   => 'Database charset',
        'test_charset_mismatch' => 'Database charset (:db) does not match selected (:conn). Polish characters may display incorrectly. Change charset to :db or leave if you know what you are doing.',
        'test_charset_auto' => 'Detected database charset: :charset. Set automatically.',
        'save_ok'           => 'Configuration saved successfully!',
        'force_403'         => 'Reconfiguration requires an authenticated administrator.',
    ],
];
$t = $T[$lang];
$__ = function($key) use ($t) { return $t[$key] ?? $key; };
$e  = function($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); };

// ─── AJAX (przed redirect check) ───
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax_action'])) {
    header('Content-Type: application/json; charset=utf-8');

    $action = $_POST['ajax_action'];

    if ($action === 'test_connection') {
        $host     = trim($_POST['host'] ?? '');
        $port     = (int)($_POST['port'] ?? 3306);
        $database = trim($_POST['database'] ?? '');
        $username = trim($_POST['username'] ?? '');
        $password = $_POST['password'] ?? '';
        $charset  = trim($_POST['charset'] ?? 'utf8mb4');

        if ($host === '' || $database === '' || $username === '') {
            echo json_encode(['success' => false, 'message' => $__('err_fill_all')]);
            exit;
        }

        try {
            $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s', $host, $port, $database, $charset);
            $pdo = new PDO($dsn, $username, $password, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_TIMEOUT => 5,
            ]);
            $version = $pdo->query('SELECT VERSION()')->fetchColumn();

            // Sprawdź wersję serwera (MySQL >= 5.7, MariaDB >= 10.2)
            $version_ok = true;
            $version_warning = '';
            if (stripos($version, 'MariaDB') !== false) {
                if (preg_match('/^(\d+\.\d+)/', $version, $m) && version_compare($m[1], '10.2', '<')) {
                    $version_ok = false;
                }
            } else {
                if (preg_match('/^(\d+\.\d+)/', $version, $m) && version_compare($m[1], '5.7', '<')) {
                    $version_ok = false;
                }
            }
            if (!$version_ok) {
                $version_warning = $__('test_version_warn');
            }

            // Auto-detekcja charset bazy danych
            $db_charset = '';
            $db_collation = '';
            $charset_match = true;
            try {
                $row = $pdo->query(
                    "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
                     FROM information_schema.SCHEMATA
                     WHERE SCHEMA_NAME = " . $pdo->quote($database)
                )->fetch();
                if ($row) {
                    $db_charset   = $row['DEFAULT_CHARACTER_SET_NAME'];
                    $db_collation = $row['DEFAULT_COLLATION_NAME'];
                    // Porównanie: utf8mb4 jest nadzbiorem utf8, więc utf8mb4 -> utf8 jest OK
                    $norm_db   = ($db_charset === 'utf8mb3') ? 'utf8' : $db_charset;
                    $norm_conn = ($charset === 'utf8mb3') ? 'utf8' : $charset;
                    if ($norm_db !== $norm_conn && !($norm_conn === 'utf8mb4' && $norm_db === 'utf8')) {
                        $charset_match = false;
                    }
                }
            } catch (PDOException $e) { /* brak dostępu do information_schema */ }

            // Sprawdź kluczowe tabele PYXIS
            $tables_found = [];
            foreach (['vUserdb', 'Userdb', 'Fv', 'Podmioty', 'Licencja'] as $tbl) {
                try {
                    $pdo->query("SELECT 1 FROM `{$tbl}` LIMIT 1");
                    $tables_found[] = $tbl;
                } catch (PDOException $e) { /* brak tabeli */ }
            }

            echo json_encode([
                'success'        => true,
                'message'        => $__('test_ok'),
                'version'        => $version,
                'version_ok'     => $version_ok,
                'version_warning'=> $version_warning,
                'tables'         => $tables_found,
                'db_charset'     => $db_charset,
                'db_collation'   => $db_collation,
                'charset_match'  => $charset_match,
            ]);
        } catch (PDOException $ex) {
            $msg = $ex->getMessage();
            if (strpos($msg, 'Access denied') !== false) {
                $msg = $__('err_access_denied');
            } elseif (strpos($msg, 'Unknown database') !== false) {
                $msg = $__('err_unknown_db');
            } elseif (strpos($msg, 'Connection refused') !== false || strpos($msg, 'No such file') !== false) {
                $msg = $__('err_conn_refused');
            } elseif (strpos($msg, 'getaddrinfo') !== false || strpos($msg, 'Name or service not known') !== false) {
                $msg = $__('err_unknown_host');
            }
            echo json_encode(['success' => false, 'message' => $msg]);
        }
        exit;
    }

    if ($action === 'save_config') {
        // Demo mode check
        $demo_file = IBOK_CFG_DIR . '/demo.cfg';
        if (file_exists($demo_file)) {
            $demo_d = @json_decode(file_get_contents($demo_file), true);
            if (!empty($demo_d['enabled'])) {
                echo json_encode(['success' => false, 'message' => 'Saving is blocked in demo mode']);
                exit;
            }
        }
        // CSRF
        if (empty($_POST['csrf_token']) || !hash_equals($_SESSION['_csrf'], $_POST['csrf_token'])) {
            echo json_encode(['success' => false, 'message' => $__('err_csrf')]);
            exit;
        }

        // EULA
        if (empty($_POST['eula_accepted'])) {
            echo json_encode(['success' => false, 'message' => $__('err_eula')]);
            exit;
        }

        $config = [
            'enabled'  => true,
            'host'     => trim($_POST['host'] ?? ''),
            'port'     => (int)($_POST['port'] ?? 3306),
            'database' => trim($_POST['database'] ?? ''),
            'username' => trim($_POST['username'] ?? ''),
            'password' => $_POST['password'] ?? '',
            'charset'  => trim($_POST['charset'] ?? 'utf8mb4'),
        ];

        if ($config['host'] === '' || $config['database'] === '' || $config['username'] === '') {
            echo json_encode(['success' => false, 'message' => $__('err_fill_all')]);
            exit;
        }

        // Test przed zapisem
        try {
            $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',
                $config['host'], $config['port'], $config['database'], $config['charset']);
            new PDO($dsn, $config['username'], $config['password'], [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_TIMEOUT => 5,
            ]);
        } catch (PDOException $ex) {
            echo json_encode(['success' => false, 'message' => $__('err_conn_fail') . $ex->getMessage()]);
            exit;
        }

        // Zapis
        if (!is_dir(IBOK_CFG_DIR)) {
            @mkdir(IBOK_CFG_DIR, 0775, true);
        }

        $json   = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        $result = @file_put_contents($db_cfg_file, $json, LOCK_EX);

        if ($result === false) {
            echo json_encode(['success' => false, 'message' => $__('err_save_fail')]);
            exit;
        }

        @chmod($db_cfg_file, 0660);

        // Log
        $log_dir = IBOK_LOGS_DIR;
        if (is_dir($log_dir) && is_writable($log_dir)) {
            $ip   = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
            $line = date('Y-m-d H:i:s') . " [info] [{$ip}] Instalator: zapisano konfigurację bazy danych ({$config['host']}:{$config['port']}/{$config['database']})\n";
            @file_put_contents($log_dir . '/ibok_' . date('Y-m') . '.log', $line, FILE_APPEND | LOCK_EX);
        }

        echo json_encode(['success' => true, 'message' => $__('save_ok')]);
        exit;
    }

    echo json_encode(['success' => false, 'message' => $__('err_unknown')]);
    exit;
}

// ─── Czy baza już skonfigurowana? ───
$already_configured = false;
if (file_exists($db_cfg_file)) {
    $content = @file_get_contents($db_cfg_file);
    if ($content !== false) {
        $cfg = @json_decode($content, true);
        if (is_array($cfg) && !empty($cfg['host']) && !empty($cfg['username']) && !empty($cfg['database'])) {
            try {
                $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',
                    $cfg['host'], (int)($cfg['port'] ?? 3306), $cfg['database'], $cfg['charset'] ?? 'utf8mb4');
                new PDO($dsn, $cfg['username'], $cfg['password'], [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_TIMEOUT => 5,
                ]);
                $already_configured = true;
            } catch (PDOException $e) {
                // Połączenie nie działa — pozwól na rekonfigurację
            }
        }
    }
}

if ($already_configured) {
    if (!isset($_GET['force'])) {
        header('Location: admin.php');
        exit;
    }

    // ?force wymaga zalogowanego admina.
    // Instalator ma własną sesję (IBOK_INSTALL) — musimy tymczasowo
    // przełączyć się na sesję admina (IBOK_SESSION) żeby sprawdzić login.
    session_write_close();

    $admin_session_name = defined('IBOK_SESSION_NAME') ? IBOK_SESSION_NAME : 'IBOK_SESSION';
    session_name($admin_session_name);
    session_start();

    $is_admin = !empty($_SESSION['logged_in'])
        && ($_SESSION['user_type'] ?? '') === 'admin'
        && (!isset($_SESSION['last_activity']) || (time() - (int)$_SESSION['last_activity']) <= 1800);

    session_write_close();

    // Wróć do sesji instalatora
    session_name('IBOK_INSTALL');
    session_start();

    if (!$is_admin) {
        header('HTTP/1.0 403 Forbidden');
        echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>403</title>';
        echo '<link href="css/nunito-sans.css" rel="stylesheet"><link rel="stylesheet" href="css/bootstrap.min.css"></head>';
        echo '<body class="d-flex align-items-center justify-content-center vh-100">';
        echo '<div class="text-center"><h1 class="text-danger">403</h1>';
        echo '<p>' . $e($__('force_403')) . '</p></div></body></html>';
        exit;
    }
}

// ─── Sprawdzenie uprawnień ───
function check_requirements(callable $__): array {
    $checks = [];

    // PHP
    $checks[] = [
        'label'  => 'PHP ' . PHP_VERSION,
        'need'   => '>= 7.4',
        'ok'     => version_compare(PHP_VERSION, '7.4.0', '>='),
        'group'  => 'php',
    ];

    // Rozszerzenia
    $exts = [
        'pdo_mysql' => 'PDO MySQL',
        'json'      => 'JSON',
        'session'   => 'Session',
        'mbstring'  => 'mbstring',
        'openssl'   => 'OpenSSL',
    ];
    foreach ($exts as $ext => $label) {
        $loaded = extension_loaded($ext);
        $checks[] = [
            'label'    => $label,
            'need'     => $__('need_required'),
            'status'   => $loaded ? $__('status_ok') : $__('status_missing'),
            'ok'       => $loaded,
            'group'    => 'ext',
            'critical' => in_array($ext, ['pdo_mysql', 'json', 'session']),
        ];
    }

    // Katalogi
    $dirs = [
        'cfg/'    => IBOK_ROOT . '/cfg',
        'logs/'   => IBOK_ROOT . '/logs',
        'db/'     => IBOK_ROOT . '/db',
        'db/2fa/' => IBOK_ROOT . '/db/2fa',
        'admin/'  => IBOK_ROOT . '/admin',
    ];
    foreach ($dirs as $label => $path) {
        $exists   = is_dir($path);
        $writable = $exists && is_writable($path);
        $checks[] = [
            'label'    => $label,
            'need'     => $__('need_rw'),
            'status'   => !$exists ? $__('status_no_dir') : ($writable ? $__('status_ok') : $__('status_no_write')),
            'ok'       => $writable,
            'group'    => 'dir',
            'critical' => ($label === 'cfg/' || $label === 'logs/'),
        ];
    }

    // database.cfg
    if (file_exists(IBOK_CFG_DIR . '/database.cfg')) {
        $w = is_writable(IBOK_CFG_DIR . '/database.cfg');
        $checks[] = [
            'label'    => 'cfg/database.cfg',
            'need'     => $__('need_write'),
            'status'   => $w ? $__('status_ok') : $__('status_no_write'),
            'ok'       => $w,
            'group'    => 'file',
            'critical' => true,
        ];
    } else {
        $can = is_dir(IBOK_CFG_DIR) && is_writable(IBOK_CFG_DIR);
        $checks[] = [
            'label'    => 'cfg/database.cfg',
            'need'     => $__('need_create'),
            'status'   => $can ? $__('status_can_create') : $__('status_no_perms'),
            'ok'       => $can,
            'group'    => 'file',
            'critical' => true,
        ];
    }

    // .htaccess ochrona katalogów wrażliwych
    $htaccess_dirs = [
        'cfg/.htaccess'  => IBOK_ROOT . '/cfg/.htaccess',
        'logs/.htaccess' => IBOK_ROOT . '/logs/.htaccess',
        'db/.htaccess'   => IBOK_ROOT . '/db/.htaccess',
    ];
    foreach ($htaccess_dirs as $label => $path) {
        $exists = file_exists($path);
        $checks[] = [
            'label'    => $label,
            'need'     => $__('htaccess_need'),
            'status'   => $exists ? $__('htaccess_present') : $__('htaccess_missing'),
            'ok'       => $exists,
            'group'    => 'htaccess',
            'critical' => false,
        ];
    }

    return $checks;
}

$checks        = check_requirements($__);
$all_ok        = !in_array(false, array_column($checks, 'ok'));
$critical_fail = false;
foreach ($checks as $c) {
    if (!$c['ok'] && !empty($c['critical'])) {
        $critical_fail = true;
        break;
    }
}

// Istniejące wartości (prefill)
$prev = ['host' => 'localhost', 'port' => 3306, 'database' => '', 'username' => '', 'password' => '', 'charset' => 'utf8mb4'];
if (file_exists($db_cfg_file)) {
    $tmp = @json_decode(@file_get_contents($db_cfg_file), true);
    if (is_array($tmp)) $prev = array_merge($prev, $tmp);
}

$nonce      = base64_encode(random_bytes(16));
$csrf_token = $_SESSION['_csrf'];
$eula_exists = file_exists(IBOK_ROOT . '/EULA.txt');
?>
<!DOCTYPE html>
<html lang="<?= $lang ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= $e($__('title')) ?></title>
    <link href="css/nunito-sans.css" rel="stylesheet">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/bootstrap-icons.min.css">
    <style nonce="<?= $nonce ?>">
        :root {
            --inst-navy: #1a365d;
            --inst-blue: #2c5282;
            --inst-accent: #3182ce;
            --inst-green: #38a169;
            --inst-red: #e53e3e;
            --inst-amber: #d69e2e;
            --inst-gradient: linear-gradient(135deg, #1a365d 0%, #2c5282 50%, #3182ce 100%);
            --inst-card: #fff;
            --inst-border: #e2e8f0;
            --inst-text: #2d3748;
            --inst-muted: #718096;
            --inst-bg-subtle: #f7fafc;
        }
        * { box-sizing: border-box; }
        body {
            margin: 0; min-height: 100vh;
            background: var(--inst-gradient);
            font-family: 'Nunito Sans', 'Segoe UI', system-ui, -apple-system, sans-serif;
            color: var(--inst-text);
            display: flex; align-items: flex-start; justify-content: center;
            padding: 2rem 1rem 3rem;
        }
        #particles-bg { position: fixed; inset: 0; z-index: 0; }
        .installer-wrap { position: relative; z-index: 2; width: 100%; max-width: 640px; }

        /* Nagłówek */
        .installer-header { text-align: center; margin-bottom: 1.5rem; animation: fadeSlideIn .6s ease-out both; }
        .installer-header h1 { color: #fff; font-size: 1.6rem; font-weight: 800; letter-spacing: -.01em; margin: 0 0 .25rem; text-shadow: 0 2px 8px rgba(0,0,0,.25); }
        .installer-header p { color: rgba(255,255,255,.7); font-size: .85rem; margin: 0; }

        /* Lang switcher */
        .lang-switch { text-align: center; margin-bottom: 1rem; animation: fadeSlideIn .6s ease-out .05s both; }
        .lang-switch a {
            display: inline-block; padding: .2rem .6rem; margin: 0 .2rem;
            border-radius: 999px; font-size: .75rem; font-weight: 700;
            text-decoration: none; transition: all .2s;
            color: rgba(255,255,255,.6); background: rgba(255,255,255,.1);
            border: 1px solid rgba(255,255,255,.15);
        }
        .lang-switch a.active { color: #fff; background: rgba(255,255,255,.25); border-color: rgba(255,255,255,.4); }
        .lang-switch a:hover { color: #fff; background: rgba(255,255,255,.2); }

        /* Stepper */
        .steps { display: flex; gap: 0; margin-bottom: 1.5rem; animation: fadeSlideIn .6s ease-out .1s both; }
        .step { flex: 1; text-align: center; position: relative; }
        .step-dot {
            width: 32px; height: 32px; border-radius: 50%;
            background: rgba(255,255,255,.15); color: rgba(255,255,255,.5);
            display: inline-flex; align-items: center; justify-content: center;
            font-weight: 700; font-size: .8rem; transition: all .4s ease;
            border: 2px solid rgba(255,255,255,.2);
        }
        .step.active .step-dot { background: #fff; color: var(--inst-navy); border-color: #fff; box-shadow: 0 0 20px rgba(255,255,255,.3); }
        .step.done .step-dot { background: var(--inst-green); color: #fff; border-color: var(--inst-green); }
        .step-label { display: block; color: rgba(255,255,255,.45); font-size: .7rem; font-weight: 600; margin-top: .35rem; transition: color .3s; }
        .step.active .step-label, .step.done .step-label { color: rgba(255,255,255,.9); }
        .step::after { content: ''; position: absolute; top: 15px; left: calc(50% + 20px); width: calc(100% - 40px); height: 2px; background: rgba(255,255,255,.15); }
        .step:last-child::after { display: none; }
        .step.done::after { background: var(--inst-green); }

        /* Karta */
        .installer-card { background: var(--inst-card); border-radius: 12px; box-shadow: 0 8px 40px rgba(0,0,0,.2), 0 1px 3px rgba(0,0,0,.08); overflow: hidden; }
        .card-section { padding: 1.5rem 1.75rem; border-bottom: 1px solid var(--inst-border); animation: fadeSlideIn .5s ease-out both; }
        .card-section:last-child { border-bottom: none; }
        .section-title { font-size: .8rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--inst-muted); margin: 0 0 1rem; display: flex; align-items: center; gap: .5rem; }
        .section-title i { font-size: 1rem; color: var(--inst-accent); }

        /* Check grid */
        .check-grid { display: grid; gap: .35rem; }
        .check-row { display: grid; grid-template-columns: 1fr auto; align-items: center; padding: .4rem .6rem; border-radius: 6px; font-size: .82rem; background: var(--inst-bg-subtle); transition: background .2s; }
        .check-row:hover { background: #edf2f7; }
        .check-label { font-weight: 600; color: var(--inst-text); display: flex; align-items: center; gap: .4rem; }
        .check-label .need { font-weight: 400; color: var(--inst-muted); font-size: .75rem; }
        .check-badge { font-size: .75rem; font-weight: 700; padding: .15rem .5rem; border-radius: 999px; white-space: nowrap; }
        .check-badge.ok { background: #c6f6d5; color: #22543d; }
        .check-badge.fail { background: #fed7d7; color: #742a2a; }
        .check-badge.warn { background: #fefcbf; color: #744210; }
        .check-group-label { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--inst-muted); padding: .5rem 0 .15rem; }
        .check-group-label:first-child { padding-top: 0; }

        /* Formularz */
        .form-row-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
        .form-row-grid .full { grid-column: 1 / -1; }
        .field-group { margin-bottom: 0; }
        .field-group label { display: block; font-size: .75rem; font-weight: 700; color: var(--inst-muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: .3rem; }
        .field-group label .req { color: var(--inst-red); }
        .field-group input, .field-group select { width: 100%; padding: .55rem .75rem; border: 1.5px solid var(--inst-border); border-radius: 8px; font-size: .88rem; font-family: inherit; color: var(--inst-text); background: #fff; transition: border-color .2s, box-shadow .2s; }
        .field-group input:focus, .field-group select:focus { outline: none; border-color: var(--inst-accent); box-shadow: 0 0 0 3px rgba(49,130,206,.15); }
        .field-group input::placeholder { color: #cbd5e0; }

        /* EULA */
        .eula-row { margin-top: 1rem; display: flex; align-items: center; gap: .5rem; font-size: .85rem; }
        .eula-row input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--inst-accent); cursor: pointer; flex-shrink: 0; }
        .eula-row a { color: var(--inst-accent); font-weight: 600; }

        /* Wynik testu */
        .test-result { margin-top: 1rem; padding: .75rem 1rem; border-radius: 8px; font-size: .85rem; display: none; animation: fadeSlideIn .3s ease-out; }
        .test-result.show { display: block; }
        .test-result.success { background: linear-gradient(135deg, #f0fff4, #c6f6d5); border: 1px solid #9ae6b4; color: #22543d; }
        .test-result.error { background: linear-gradient(135deg, #fff5f5, #fed7d7); border: 1px solid #feb2b2; color: #742a2a; }
        .test-result.warning { background: linear-gradient(135deg, #fffff0, #fefcbf); border: 1px solid #ecc94b; color: #744210; }
        .test-result .tables-list { margin-top: .4rem; font-size: .78rem; opacity: .85; }
        .test-result .tables-list span { display: inline-block; background: rgba(255,255,255,.6); padding: .1rem .4rem; border-radius: 4px; margin: .1rem .15rem; font-weight: 600; }

        /* Przyciski */
        .btn-actions { display: flex; gap: .75rem; margin-top: 1.25rem; flex-wrap: wrap; }
        .btn-inst { display: inline-flex; align-items: center; gap: .4rem; padding: .6rem 1.25rem; border-radius: 8px; border: none; font-size: .85rem; font-weight: 700; font-family: inherit; cursor: pointer; transition: all .2s ease; }
        .btn-inst:disabled { opacity: .5; cursor: not-allowed; }
        .btn-test { background: var(--inst-bg-subtle); color: var(--inst-accent); border: 1.5px solid var(--inst-border); }
        .btn-test:hover:not(:disabled) { background: #edf2f7; border-color: var(--inst-accent); }
        .btn-save { background: var(--inst-gradient); color: #fff; flex: 1; justify-content: center; }
        .btn-save:hover:not(:disabled) { opacity: .9; box-shadow: 0 4px 15px rgba(49,130,206,.35); }
        .btn-inst .spinner { display: none; width: 14px; height: 14px; border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%; animation: spin .6s linear infinite; }
        .btn-inst.loading .spinner { display: inline-block; }
        .btn-inst.loading .btn-icon { display: none; }

        /* Sukces */
        .success-overlay { display: none; position: fixed; inset: 0; z-index: 100; background: var(--inst-gradient); align-items: center; justify-content: center; flex-direction: column; gap: 1rem; text-align: center; color: #fff; animation: fadeIn .4s ease-out; }
        .success-overlay.show { display: flex; }
        .success-icon { width: 80px; height: 80px; border-radius: 50%; background: rgba(255,255,255,.15); display: flex; align-items: center; justify-content: center; font-size: 2.5rem; animation: popIn .5s ease-out .2s both; }
        .success-overlay h2 { font-size: 1.4rem; font-weight: 800; margin: 0; animation: fadeSlideIn .5s ease-out .4s both; }
        .success-overlay p { color: rgba(255,255,255,.7); font-size: .9rem; margin: 0; animation: fadeSlideIn .5s ease-out .5s both; }
        .success-overlay .btn-go { margin-top: .5rem; display: inline-flex; align-items: center; gap: .4rem; padding: .7rem 2rem; background: #fff; color: var(--inst-navy); border: none; border-radius: 8px; font-size: .9rem; font-weight: 700; font-family: inherit; cursor: pointer; text-decoration: none; animation: fadeSlideIn .5s ease-out .6s both; transition: box-shadow .2s; }
        .success-overlay .btn-go:hover { box-shadow: 0 4px 20px rgba(255,255,255,.3); }

        @keyframes fadeSlideIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
        @keyframes popIn { 0% { opacity: 0; transform: scale(.5); } 70% { transform: scale(1.1); } 100% { opacity: 1; transform: scale(1); } }
        @keyframes spin { to { transform: rotate(360deg); } }

        @media (max-width: 576px) {
            body { padding: 1rem .5rem 2rem; }
            .card-section { padding: 1.25rem 1rem; }
            .form-row-grid { grid-template-columns: 1fr; }
            .form-row-grid .full { grid-column: 1; }
            .btn-actions { flex-direction: column; }
            .btn-save { width: 100%; }
            .step-label { font-size: .6rem; }
        }
    </style>
</head>
<body>
    <div id="particles-bg"></div>

    <div class="installer-wrap">
        <div class="installer-header">
            <h1><i class="bi bi-speedometer2"></i> i-BOK 5</h1>
            <p><?= $e($__('subtitle')) ?></p>
        </div>

        <div class="lang-switch">
            <?php foreach (['pl' => 'PL', 'en' => 'EN'] as $code => $label): ?>
                <a href="?lang=<?= $code ?><?= isset($_GET['force']) ? '&force' : '' ?>" class="<?= $lang === $code ? 'active' : '' ?>"><?= $label ?></a>
            <?php endforeach; ?>
        </div>

        <div class="steps">
            <div class="step <?= $all_ok ? 'done' : 'active' ?>" id="step1">
                <span class="step-dot"><?= $all_ok ? '<i class="bi bi-check-lg"></i>' : '1' ?></span>
                <span class="step-label"><?= $e($__('step_requirements')) ?></span>
            </div>
            <div class="step <?= $all_ok ? 'active' : '' ?>" id="step2">
                <span class="step-dot">2</span>
                <span class="step-label"><?= $e($__('step_database')) ?></span>
            </div>
            <div class="step" id="step3">
                <span class="step-dot">3</span>
                <span class="step-label"><?= $e($__('step_done')) ?></span>
            </div>
        </div>

        <div class="installer-card">

            <!-- Wymagania -->
            <div class="card-section" style="animation-delay:.15s">
                <div class="section-title">
                    <i class="bi bi-shield-check"></i> <?= $e($__('section_requirements')) ?>
                </div>
                <div class="check-grid">
                    <div class="check-group-label"><?= $e($__('group_php')) ?></div>
                    <?php foreach ($checks as $c): ?>
                        <?php if (!in_array($c['group'], ['php', 'ext'])) continue; ?>
                        <div class="check-row">
                            <span class="check-label">
                                <?= $e($c['label']) ?>
                                <span class="need">(<?= $e($c['need']) ?>)</span>
                            </span>
                            <span class="check-badge <?= $c['ok'] ? 'ok' : 'fail' ?>">
                                <?= $c['ok'] ? '<i class="bi bi-check-lg"></i> ' . $e($__('status_ok')) : '<i class="bi bi-x-lg"></i> ' . $e($c['status'] ?? $__('status_missing')) ?>
                            </span>
                        </div>
                    <?php endforeach; ?>

                    <div class="check-group-label"><?= $e($__('group_dirs')) ?></div>
                    <?php foreach ($checks as $c): ?>
                        <?php if ($c['group'] !== 'dir' && $c['group'] !== 'file') continue; ?>
                        <div class="check-row">
                            <span class="check-label">
                                <code style="font-size:.8em;color:var(--inst-accent)"><?= $e($c['label']) ?></code>
                                <span class="need">(<?= $e($c['need']) ?>)</span>
                            </span>
                            <span class="check-badge <?= $c['ok'] ? 'ok' : (!empty($c['critical']) ? 'fail' : 'warn') ?>">
                                <?= $c['ok'] ? '<i class="bi bi-check-lg"></i> ' . $e($__('status_ok')) : '<i class="bi bi-x-lg"></i> ' . $e($c['status'] ?? '') ?>
                            </span>
                        </div>
                    <?php endforeach; ?>

                    <div class="check-group-label"><?= $e($__('group_htaccess')) ?></div>
                    <?php foreach ($checks as $c): ?>
                        <?php if ($c['group'] !== 'htaccess') continue; ?>
                        <div class="check-row">
                            <span class="check-label">
                                <code style="font-size:.8em;color:var(--inst-accent)"><?= $e($c['label']) ?></code>
                                <span class="need">(<?= $e($c['need']) ?>)</span>
                            </span>
                            <span class="check-badge <?= $c['ok'] ? 'ok' : 'warn' ?>">
                                <?= $c['ok'] ? '<i class="bi bi-check-lg"></i> ' . $e($c['status']) : '<i class="bi bi-exclamation-triangle"></i> ' . $e($c['status']) ?>
                            </span>
                        </div>
                    <?php endforeach; ?>
                </div>

                <?php if ($critical_fail): ?>
                    <div class="test-result error show" style="margin-top:1rem">
                        <strong><i class="bi bi-exclamation-triangle me-1"></i> <?= $e($__('critical_title')) ?></strong><br>
                        <?= $__('critical_desc') ?>
                    </div>
                <?php endif; ?>
            </div>

            <!-- Baza danych -->
            <div class="card-section" style="animation-delay:.25s">
                <div class="section-title">
                    <i class="bi bi-database"></i> <?= $e($__('section_database')) ?>
                </div>

                <form id="dbForm" autocomplete="off">
                    <input type="hidden" name="csrf_token" id="f_csrf" value="<?= $e($csrf_token) ?>">
                    <div class="form-row-grid">
                        <div class="field-group">
                            <label><?= $e($__('field_host')) ?> <span class="req">*</span></label>
                            <input type="text" name="host" id="f_host" value="<?= $e($prev['host']) ?>" placeholder="<?= $e($__('ph_host')) ?>" required>
                        </div>
                        <div class="field-group">
                            <label><?= $e($__('field_port')) ?></label>
                            <input type="number" name="port" id="f_port" value="<?= (int)$prev['port'] ?>" min="1" max="65535">
                        </div>
                        <div class="field-group full">
                            <label><?= $e($__('field_database')) ?> <span class="req">*</span></label>
                            <input type="text" name="database" id="f_database" value="<?= $e($prev['database']) ?>" placeholder="<?= $e($__('ph_database')) ?>" required>
                        </div>
                        <div class="field-group">
                            <label><?= $e($__('field_username')) ?> <span class="req">*</span></label>
                            <input type="text" name="username" id="f_username" value="<?= $e($prev['username']) ?>" placeholder="<?= $e($__('ph_username')) ?>" required>
                        </div>
                        <div class="field-group">
                            <label><?= $e($__('field_password')) ?></label>
                            <input type="password" name="password" id="f_password" value="<?= $e($prev['password']) ?>" placeholder="<?= $e($__('ph_password')) ?>">
                        </div>
                        <div class="field-group">
                            <label><?= $e($__('field_charset')) ?></label>
                            <select name="charset" id="f_charset">
                                <option value="utf8mb4" <?= ($prev['charset'] ?? 'utf8mb4') === 'utf8mb4' ? 'selected' : '' ?>>utf8mb4</option>
                                <option value="utf8" <?= ($prev['charset'] ?? '') === 'utf8' ? 'selected' : '' ?>>utf8</option>
                                <option value="latin2" <?= ($prev['charset'] ?? '') === 'latin2' ? 'selected' : '' ?>>latin2</option>
                                <option value="cp1250" <?= ($prev['charset'] ?? '') === 'cp1250' ? 'selected' : '' ?>>cp1250</option>
                            </select>
                        </div>
                    </div>

                    <div class="eula-row">
                        <input type="checkbox" id="f_eula">
                        <label for="f_eula" style="text-transform:none;font-weight:500;color:var(--inst-text);font-size:.85rem;margin:0">
                            <?= $e($__('eula_accept')) ?>
                            <?php if ($eula_exists): ?>
                                <a href="EULA.txt" target="_blank"><?= $e($__('eula_link')) ?></a>
                            <?php else: ?>
                                <?= $e($__('eula_link')) ?>
                            <?php endif; ?>
                        </label>
                    </div>

                    <div id="testResult" class="test-result"></div>

                    <div class="btn-actions">
                        <button type="button" class="btn-inst btn-test" id="btnTest" <?= $critical_fail ? 'disabled' : '' ?>>
                            <span class="spinner"></span>
                            <i class="bi bi-plug btn-icon"></i>
                            <?= $e($__('btn_test')) ?>
                        </button>
                        <button type="button" class="btn-inst btn-save" id="btnSave" disabled>
                            <span class="spinner"></span>
                            <i class="bi bi-check-circle btn-icon"></i>
                            <?= $e($__('btn_save')) ?>
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>

    <!-- Overlay sukcesu -->
    <div class="success-overlay" id="successOverlay">
        <div class="success-icon"><i class="bi bi-check-lg"></i></div>
        <h2><?= $e($__('success_title')) ?></h2>
        <p><?= $e($__('success_desc')) ?></p>
        <a href="admin.php" class="btn-go"><i class="bi bi-arrow-right"></i> <?= $e($__('success_btn')) ?></a>
    </div>

    <script nonce="<?= $nonce ?>" src="js/particles.min.js"></script>
    <script nonce="<?= $nonce ?>">
    if (typeof particlesJS !== 'undefined') {
        particlesJS("particles-bg", {
            particles: {
                number: { value: 50, density: { enable: true, value_area: 1000 } },
                color: { value: "#ffffff" },
                shape: { type: "circle" },
                opacity: { value: 0.2, random: true, anim: { enable: true, speed: 0.5, opacity_min: 0.06, sync: false } },
                size: { value: 3, random: true },
                line_linked: { enable: true, distance: 130, color: "#ffffff", opacity: 0.12, width: 1 },
                move: { enable: true, speed: 0.8, direction: "none", random: true, out_mode: "out" }
            },
            interactivity: {
                detect_on: "canvas",
                events: { onhover: { enable: true, mode: "grab" }, onclick: { enable: false }, resize: true },
                modes: { grab: { distance: 150, line_linked: { opacity: 0.3 } } }
            },
            retina_detect: true
        });
    }

    (function() {
        const LANG = <?= json_encode($t) ?>;
        const btnTest = document.getElementById('btnTest');
        const btnSave = document.getElementById('btnSave');
        const result  = document.getElementById('testResult');
        const eulaBox = document.getElementById('f_eula');
        let testPassed = false;

        function getFormData() {
            return {
                host: document.getElementById('f_host').value.trim(),
                port: document.getElementById('f_port').value,
                database: document.getElementById('f_database').value.trim(),
                username: document.getElementById('f_username').value.trim(),
                password: document.getElementById('f_password').value,
                charset: document.getElementById('f_charset').value,
            };
        }

        function showResult(cls, html) {
            result.className = 'test-result show ' + cls;
            result.innerHTML = html;
        }

        function setLoading(btn, on) {
            btn.classList.toggle('loading', on);
            btn.disabled = on;
        }

        function updateSaveBtn() {
            btnSave.disabled = !(testPassed && eulaBox.checked);
        }

        function updateSteps(step) {
            document.querySelectorAll('.step').forEach((el, i) => {
                el.classList.remove('active', 'done');
                if (i < step - 1) el.classList.add('done');
                else if (i === step - 1) el.classList.add('active');
            });
            document.querySelectorAll('.step.done .step-dot').forEach(dot => {
                if (!dot.querySelector('i')) dot.innerHTML = '<i class="bi bi-check-lg"></i>';
            });
        }

        // Reset testu przy zmianie pól
        document.querySelectorAll('#dbForm input:not(#f_eula):not(#f_csrf), #dbForm select').forEach(el => {
            el.addEventListener('input', function() {
                testPassed = false;
                updateSaveBtn();
                result.classList.remove('show');
            });
        });

        eulaBox.addEventListener('change', updateSaveBtn);

        // Test
        btnTest.addEventListener('click', function() {
            const data = getFormData();
            if (!data.host || !data.database || !data.username) {
                showResult('error', '<i class="bi bi-exclamation-triangle me-1"></i> ' + LANG.err_fill_required);
                return;
            }

            setLoading(btnTest, true);
            result.classList.remove('show');

            const fd = new FormData();
            fd.append('ajax_action', 'test_connection');
            for (const k in data) fd.append(k, data[k]);

            fetch('install.php', { method: 'POST', body: fd })
                .then(r => r.json())
                .then(res => {
                    setLoading(btnTest, false);
                    if (res.success) {
                        testPassed = true;
                        updateSaveBtn();
                        let html = '<strong><i class="bi bi-check-circle me-1"></i> ' + res.message + '</strong>';
                        if (res.version) html += '<br><small>' + LANG.test_server + ': ' + res.version + '</small>';

                        if (!res.version_ok && res.version_warning) {
                            html += '<div style="margin-top:.4rem;color:#744210"><i class="bi bi-exclamation-triangle me-1"></i>' + res.version_warning + '</div>';
                        }

                        // Charset bazy — auto-detekcja
                        let hasCharsetWarn = false;
                        if (res.db_charset) {
                            const charsetSelect = document.getElementById('f_charset');
                            const connCharset = charsetSelect.value;
                            html += '<div style="margin-top:.4rem"><small>' + LANG.test_db_charset + ': <strong>' + res.db_charset + '</strong>';
                            if (res.db_collation) html += ' (' + res.db_collation + ')';
                            html += '</small></div>';

                            if (!res.charset_match) {
                                // Spróbuj auto-ustawić charset z bazy
                                const dbCs = res.db_charset.toLowerCase().replace('utf8mb3', 'utf8');
                                const options = Array.from(charsetSelect.options).map(o => o.value);
                                if (options.includes(dbCs)) {
                                    charsetSelect.value = dbCs;
                                    html += '<div style="margin-top:.3rem;color:#22543d"><i class="bi bi-arrow-repeat me-1"></i>' + LANG.test_charset_auto.replace(':charset', res.db_charset) + '</div>';
                                } else {
                                    hasCharsetWarn = true;
                                    html += '<div style="margin-top:.3rem;color:#744210"><i class="bi bi-exclamation-triangle me-1"></i>' + LANG.test_charset_mismatch.replace(':db', res.db_charset).replace(':conn', connCharset) + '</div>';
                                }
                            }
                        }

                        if (res.tables && res.tables.length > 0) {
                            html += '<div class="tables-list">' + LANG.test_tables_found + ': ';
                            res.tables.forEach(t => html += '<span>' + t + '</span>');
                            html += '</div>';
                        } else {
                            html += '<div class="tables-list" style="color:#744210"><i class="bi bi-exclamation-triangle me-1"></i> ' + LANG.test_no_tables + '</div>';
                        }

                        showResult((res.version_ok && !hasCharsetWarn) ? 'success' : 'warning', html);
                        updateSteps(2);
                    } else {
                        testPassed = false;
                        updateSaveBtn();
                        showResult('error', '<i class="bi bi-x-circle me-1"></i> ' + res.message);
                    }
                })
                .catch(() => {
                    setLoading(btnTest, false);
                    showResult('error', '<i class="bi bi-x-circle me-1"></i> ' + LANG.err_comm);
                });
        });

        // Save
        btnSave.addEventListener('click', function() {
            if (!testPassed) return;
            if (!eulaBox.checked) {
                showResult('error', '<i class="bi bi-exclamation-triangle me-1"></i> ' + LANG.eula_required);
                return;
            }

            const data = getFormData();
            setLoading(btnSave, true);

            const fd = new FormData();
            fd.append('ajax_action', 'save_config');
            fd.append('csrf_token', document.getElementById('f_csrf').value);
            fd.append('eula_accepted', '1');
            for (const k in data) fd.append(k, data[k]);

            fetch('install.php', { method: 'POST', body: fd })
                .then(r => r.json())
                .then(res => {
                    setLoading(btnSave, false);
                    if (res.success) {
                        updateSteps(3);
                        document.getElementById('successOverlay').classList.add('show');
                    } else {
                        showResult('error', '<i class="bi bi-x-circle me-1"></i> ' + res.message);
                    }
                })
                .catch(() => {
                    setLoading(btnSave, false);
                    showResult('error', '<i class="bi bi-x-circle me-1"></i> ' + LANG.err_comm);
                });
        });
    })();
    </script>
</body>
</html>
