<?php
/**
 * @intranet-file instalacion/index.php
 * @intranet-file-version 2026.03.13.1
 * @intranet-file-date 2026-03-13
 * @intranet-file-owner CSIF Intranet
 */
session_start();
require_once __DIR__ . '/lib.php';
enforce_https();

$lock = dirname(__DIR__) . '/config/installed.lock';
if (file_exists($lock) && (!isset($_GET['reinstall']) || $_GET['reinstall'] !== '1')) {
  http_response_code(200);
  echo "<!doctype html><meta charset='utf-8'><link rel='stylesheet' href='assets/style.css'>";
  echo "<div class='wrap'><div class='card'>";
  echo "<h2>La intranet ya esta instalada.</h2>";
  echo "<p>Si necesitas reinstalar en un entorno controlado, borra <code>config/installed.lock</code> y vuelve a entrar aqui.</p>";
  echo "<a class='btn primary' href='../login.php'>Ir al login</a>";
  echo "</div></div>";
  exit;
}

$step = max(1, (int) ($_GET['step'] ?? 1));
$sessionLdapMode = (string)($_SESSION['ldap_mode'] ?? 'ldap');
$sessionLdapServer = (string)($_SESSION['ldap']['server'] ?? '');
$installUsesLdaps = $sessionLdapMode === 'ldaps' || ($sessionLdapServer !== '' && ldap_is_ldaps($sessionLdapServer));
$finalInstallStep = $installUsesLdaps ? 6 : 5;

function step_badge(int $n, string $label, int $cur): string
{
  $cls = $n === $cur ? "step active" : "step";
  return "<span class='{$cls}'>Paso {$n}: " . h($label) . "</span>";
}

function install_progress_start(): void
{
  @ini_set('output_buffering', 'off');
  @ini_set('zlib.output_compression', '0');
  @ini_set('implicit_flush', '1');
  while (ob_get_level() > 0) {
    @ob_end_flush();
  }
  @ob_implicit_flush(true);

  echo "<!doctype html><html lang='es'><head><meta charset='utf-8'>";
  echo "<meta name='viewport' content='width=device-width,initial-scale=1'>";
  echo "<title>Instalando...</title><link rel='stylesheet' href='assets/style.css'></head><body>";
  echo "<div class='wrap'><div class='card'>";
  echo "<h2>Instalando...</h2>";
  echo "<p><small>Se ira mostrando el progreso.</small></p>";
  echo "<div id='log' style='font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, &quot;Liberation Mono&quot;, &quot;Courier New&quot;, monospace; font-size:13px; line-height:1.45; background:#0b1220; color:#e5e7eb; padding:12px; border-radius:10px; max-height:55vh; overflow:auto;'></div>";
  echo "<script>
    function addLine(t,cls){
      var d=document.createElement('div');
      d.textContent=t;
      if(cls) d.style.color=cls;
      document.getElementById('log').appendChild(d);
      window.scrollTo(0,document.body.scrollHeight);
    }
  </script>";
  flush();
}

function install_progress_line(string $msg, string $color = ''): void
{
  $safe = json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  $safeColor = json_encode($color, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  echo "<script>addLine($safe,$safeColor);</script>";
  flush();
}

function is_1227(Throwable $e): bool
{
  $m = $e->getMessage();
  return (strpos($m, ' 1227 ') !== false) ||
    (strpos($m, '1227') !== false) ||
    (stripos($m, 'SET USER') !== false) ||
    (stripos($m, 'SUPER') !== false);
}

function normalize_hex_color(string $color, string $default = '#067B3B'): string
{
  $color = trim($color);
  if ($color === '') {
    return strtoupper($default);
  }

  if ($color[0] !== '#') {
    $color = '#' . $color;
  }

  if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
    return strtoupper($default);
  }

  return strtoupper($color);
}

function normalize_base_path(string $path): string
{
  $path = trim($path);
  if ($path === '' || $path === '/') {
    return '';
  }
  $path = str_replace('\\', '/', $path);
  return '/' . trim($path, '/');
}

function normalize_external_url(string $url): string
{
  $url = trim($url);
  if ($url === '') {
    return '';
  }

  if (!preg_match('#^https?://#i', $url)) {
    $url = 'https://' . ltrim($url, '/');
  }

  return $url;
}

const INSTALL_REF_PRIMARY = '#067B3B';
const INSTALL_REF_PRIMARY_DARK = '#045026';
const INSTALL_REF_PRIMARY_LIGHT = '#EAF8F0';
const INSTALL_REF_ACCENT = '#0A9D4C';
const INSTALL_REF_BG_SIDEBAR = '#03351A';
const INSTALL_REF_BORDER_DARK = '#056332';
const INSTALL_REF_BG_SIDEBAR_HEADER = 'rgba(0, 0, 0, 0.30)';
const INSTALL_REF_BORDER_FOCUS_ALPHA = 0.30;

function hex_to_rgb(string $hex): array
{
  $hex = ltrim($hex, '#');
  return [
    hexdec(substr($hex, 0, 2)),
    hexdec(substr($hex, 2, 2)),
    hexdec(substr($hex, 4, 2)),
  ];
}

function rgb_to_hex(int $r, int $g, int $b): string
{
  $r = max(0, min(255, $r));
  $g = max(0, min(255, $g));
  $b = max(0, min(255, $b));
  return sprintf('#%02X%02X%02X', $r, $g, $b);
}

function rgb_to_hsl(int $r, int $g, int $b): array
{
  $r /= 255;
  $g /= 255;
  $b /= 255;
  $max = max($r, $g, $b);
  $min = min($r, $g, $b);
  $h = 0.0;
  $s = 0.0;
  $l = ($max + $min) / 2;

  if ($max !== $min) {
    $d = $max - $min;
    $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
    switch ($max) {
      case $r:
        $h = ($g - $b) / $d + ($g < $b ? 6 : 0);
        break;
      case $g:
        $h = ($b - $r) / $d + 2;
        break;
      default:
        $h = ($r - $g) / $d + 4;
        break;
    }
    $h /= 6;
  }

  return [$h * 360, $s * 100, $l * 100];
}

function hue_to_rgb(float $p, float $q, float $t): float
{
  if ($t < 0) $t += 1;
  if ($t > 1) $t -= 1;
  if ($t < 1 / 6) return $p + ($q - $p) * 6 * $t;
  if ($t < 1 / 2) return $q;
  if ($t < 2 / 3) return $p + ($q - $p) * (2 / 3 - $t) * 6;
  return $p;
}

function hsl_to_rgb(float $h, float $s, float $l): array
{
  $h = fmod(($h + 360.0), 360.0) / 360.0;
  $s = max(0.0, min(100.0, $s)) / 100.0;
  $l = max(0.0, min(100.0, $l)) / 100.0;

  if ($s == 0.0) {
    $v = (int) round($l * 255);
    return [$v, $v, $v];
  }

  $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
  $p = 2 * $l - $q;

  $r = hue_to_rgb($p, $q, $h + 1 / 3);
  $g = hue_to_rgb($p, $q, $h);
  $b = hue_to_rgb($p, $q, $h - 1 / 3);

  return [(int) round($r * 255), (int) round($g * 255), (int) round($b * 255)];
}

function delta_from_reference(string $refTarget, string $refPrimary = INSTALL_REF_PRIMARY): array
{
  [$ph, $ps, $pl] = rgb_to_hsl(...hex_to_rgb($refPrimary));
  [$th, $ts, $tl] = rgb_to_hsl(...hex_to_rgb($refTarget));

  $dh = $th - $ph;
  while ($dh > 180) $dh -= 360;
  while ($dh < -180) $dh += 360;

  return [$dh, $ts - $ps, $tl - $pl];
}

function apply_hsl_delta(string $newPrimary, array $delta): string
{
  [$h, $s, $l] = rgb_to_hsl(...hex_to_rgb($newPrimary));
  [$dh, $ds, $dl] = $delta;
  [$r, $g, $b] = hsl_to_rgb($h + $dh, $s + $ds, $l + $dl);
  return rgb_to_hex($r, $g, $b);
}

function rgba_from_hex(string $hex, float $alpha): string
{
  [$r, $g, $b] = hex_to_rgb($hex);
  $alpha = max(0, min(1, $alpha));
  return sprintf('rgba(%d, %d, %d, %.2f)', $r, $g, $b, $alpha);
}

function build_theme_palette(string $primary): array
{
  $primary = normalize_hex_color($primary, INSTALL_REF_PRIMARY);

  return [
    'primary' => $primary,
    'primary_dark' => apply_hsl_delta($primary, delta_from_reference(INSTALL_REF_PRIMARY_DARK)),
    'primary_light' => apply_hsl_delta($primary, delta_from_reference(INSTALL_REF_PRIMARY_LIGHT)),
    'accent' => apply_hsl_delta($primary, delta_from_reference(INSTALL_REF_ACCENT)),
    'bg_sidebar' => apply_hsl_delta($primary, delta_from_reference(INSTALL_REF_BG_SIDEBAR)),
    'border_dark' => apply_hsl_delta($primary, delta_from_reference(INSTALL_REF_BORDER_DARK)),
    'bg_sidebar_header' => INSTALL_REF_BG_SIDEBAR_HEADER,
    'border_focus' => rgba_from_hex($primary, INSTALL_REF_BORDER_FOCUS_ALPHA),
  ];
}

function write_theme_vars_css(string $primaryColor): string
{
  $themePath = dirname(__DIR__) . '/styles/css/theme-vars.css';
  $p = build_theme_palette($primaryColor);

  $content = ":root {
"
    . "    --primary: {$p['primary']};
"
    . "    --primary-dark: {$p['primary_dark']};
"
    . "    --primary-light: {$p['primary_light']};
"
    . "    --accent: {$p['accent']};
"
    . "    --bg-sidebar: {$p['bg_sidebar']};
"
    . "    --bg-sidebar-header: {$p['bg_sidebar_header']};
"
    . "    --border-dark: {$p['border_dark']};
"
    . "    --border-focus: {$p['border_focus']};
"
    . "}
";

  if (file_put_contents($themePath, $content) === false) {
    throw new RuntimeException("No se pudo escribir {$themePath}. Revisa permisos.");
  }

  return $content;
}



function ldaps_manual_cert_filename(): string
{
  $pending = $_SESSION['ldaps_ca_pending'] ?? [];
  $uploaded = trim((string)($pending['uploaded_name'] ?? ''));
  if ($uploaded !== '') {
    return preg_replace('/\.(cer|crt|pem)$/i', '.crt', basename($uploaded));
  }
  return 'intranet-ldaps-ca.crt';
}

function ldaps_manual_install_commands(): array
{
  $pending = $_SESSION['ldaps_ca_pending'] ?? [];
  $source = trim((string)($pending['temp_path'] ?? ''));
  $source = $source !== '' ? $source : '/ruta/al/certificado/subido.cer';
  $target = '/usr/local/share/ca-certificates/' . ldaps_manual_cert_filename();

  return [
    "sudo cp {$source} {$target}",
    "sudo chmod 644 {$target}",
    'sudo update-ca-certificates',
  ];
}

function pdo_admin_connect(array $admin): PDO
{
  $host = $admin['host'] ?? 'localhost';
  $port = (int) ($admin['port'] ?? 3306);
  $user = $admin['user'] ?? '';
  $pass = $admin['pass'] ?? '';
  $dsn = "mysql:host={$host};port={$port};charset=utf8mb4";

  return new PDO($dsn, $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  ]);
}

function admin_fix_suggestions(string $adminUser, string $adminHost, string $dbName, string $appUser, string $appHost): array
{
  $adminUser = addslashes($adminUser);
  $adminHost = addslashes($adminHost);
  $dbName = addslashes($dbName);
  $appUser = addslashes($appUser);
  $appHost = addslashes($appHost);

  return [
    "1) Comprueba privilegios actuales:\n   SHOW GRANTS FOR '{$adminUser}'@'{$adminHost}';",
    "2) Si el admin NO tiene WITH GRANT OPTION (debe hacerlo un superusuario/root):\n   GRANT ALL PRIVILEGES ON *.* TO '{$adminUser}'@'{$adminHost}' WITH GRANT OPTION;\n   FLUSH PRIVILEGES;",
    "3) Alternativa segura: crear usuario de aplicacion y permisos con root/DBA:\n   CREATE DATABASE IF NOT EXISTS `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n   CREATE USER IF NOT EXISTS '{$appUser}'@'{$appHost}' IDENTIFIED BY 'TU_PASSWORD';\n   GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP ON `{$dbName}`.* TO '{$appUser}'@'{$appHost}';\n   FLUSH PRIVILEGES;",
    "4) Si NO puedes usar root/DBA, desmarca \"Crear BD y usuario...\" y usa un usuario de aplicacion ya creado con permisos sobre `{$dbName}`.",
  ];
}

function test_admin_privileges(PDO $pdoAdmin, array $db, array $admin, array &$notices, array &$errors): void
{
  $dbName = $db['name'] ?? '';
  $appHost = $db['user_host'] ?? 'localhost';

  $tmpUser = '__inst_test_user';
  $tmpPass = bin2hex(random_bytes(6));
  $tmpHost = $appHost ?: 'localhost';

  try {
    $pdoAdmin->query("SELECT 1");

    if ($dbName !== '') {
      $pdoAdmin->exec("CREATE DATABASE IF NOT EXISTS `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
      $notices[] = "Privilegios admin: CREATE DATABASE sobre `{$dbName}`: OK";
    }

    try {
      $pdoAdmin->exec("DROP USER IF EXISTS `{$tmpUser}`@`{$tmpHost}`");
    } catch (Throwable $e) {
    }

    $pdoAdmin->exec("CREATE USER `{$tmpUser}`@`{$tmpHost}` IDENTIFIED BY '{$tmpPass}'");
    $notices[] = "Privilegios admin: CREATE USER: OK";

    if ($dbName !== '') {
      $pdoAdmin->exec("GRANT SELECT, INSERT, UPDATE, DELETE ON `{$dbName}`.* TO `{$tmpUser}`@`{$tmpHost}`");
      $notices[] = "Privilegios admin: GRANT sobre `{$dbName}`.*: OK";
    }

    $pdoAdmin->exec("DROP USER IF EXISTS `{$tmpUser}`@`{$tmpHost}`");

  } catch (Throwable $e) {
    $errors[] = "El usuario admin conecta, pero NO tiene permisos suficientes para crear usuario/permisos o BD. Detalle: " . $e->getMessage();

    $adminUser = $admin['user'] ?? '';
    $adminHost = 'localhost';
    $appUser = $db['user'] ?? '';
    $appHost2 = $db['user_host'] ?? 'localhost';

    $errors[] = "Sugerencias para corregir permisos (ejecutar con un usuario root/DBA):";
    foreach (admin_fix_suggestions($adminUser, $adminHost, $dbName, $appUser, $appHost2) as $s) {
      $errors[] = $s;
    }

    try {
      $pdoAdmin->exec("DROP USER IF EXISTS `{$tmpUser}`@`{$tmpHost}`");
    } catch (Throwable $e2) {
    }
  }
}

function installer_preserve_optional_defines(string $existingConfigPath): array
{
  $preserved = [];
  if (!is_file($existingConfigPath)) {
    return $preserved;
  }

  $raw = (string) file_get_contents($existingConfigPath);
  $optionalFlags = [];

  foreach ($optionalFlags as $constName) {
    if (preg_match('/define\s*\(\s*[\'\"]' . preg_quote($constName, '/') . '[\'\"]\s*,\s*(.*?)\s*\)\s*;/is', $raw, $m)) {
      $preserved[$constName] = trim($m[1]);
    }
  }

  return $preserved;
}

function write_secret_config_php(array $ldap, array $db): string
{
  $path = dirname(__DIR__) . '/config/secrets.local.php';
  $content =
    "<?php\n"
    . "define('LDAP_ADMIN_PASS', " . var_export((string)($ldap['bind_pass'] ?? ''), true) . ");\n"
    . "define('DB_PASS', " . var_export((string)($db['pass'] ?? ''), true) . ");\n";

  if (file_put_contents($path, $content) === false) {
    throw new RuntimeException("No se pudo escribir {$path}. Revisa permisos.");
  }

  return $content;
}

function write_common_config_php(array $ldap, array $db, array $branding, string $appBasePath): string
{
  $path = dirname(__DIR__) . '/common/config.php';
  $preservedDefines = installer_preserve_optional_defines($path);

  $ldapServer = $ldap['server'] ?? '';
  $ldapDomain = $ldap['domain'] ?? '';
  $ldapBaseDn = $ldap['base_dn'] ?? '';
  $ldapAdminDn = $ldap['bind_dn'] ?? '';

  $dbHost = $db['host'] ?? 'localhost';
  $dbPort = (int)($db['port'] ?? 3306);
  $dbName = $db['name'] ?? '';
  $dbUser = $db['user'] ?? '';

  $siteName = trim((string)($branding['site_name'] ?? '')) ?: 'Intranet';
  $organizationName = trim((string)($branding['organization_name'] ?? '')) ?: 'Intranet';
  $territoryName = trim((string)($branding['territory_name'] ?? ''));
  $unitName = trim((string)($branding['unit_name'] ?? '')) ?: $siteName;
  $websiteUrl = normalize_external_url((string)($branding['website_url'] ?? ''));
  $welcomeEntity = trim((string)($branding['welcome_entity'] ?? '')) ?: 'la organizacion';
  $appBasePath = normalize_base_path($appBasePath);

  $content = <<<'PHP'
<?php
// common/config.php (generado por instalador)
// Este fichero solo contiene configuracion no sensible y carga overrides
// locales/externos para secretos.

$intranetConfigOverrides = [
    '/etc/intranet/intranet-config.php',
    '/etc/csif/intranet-config.php',
    dirname(__DIR__) . '/config/secrets.local.php',
];

foreach ($intranetConfigOverrides as $overridePath) {
    if (is_file($overridePath) && is_readable($overridePath)) {
        require_once $overridePath;
    }
}

if (!function_exists('intranet_config_env_value')) {
    function intranet_config_env_value(string $name, string $default = ''): string
    {
        $value = getenv($name);
        if ($value !== false && $value !== '') {
            return (string)$value;
        }

        if (isset($_ENV[$name]) && $_ENV[$name] !== '') {
            return (string)$_ENV[$name];
        }

        return $default;
    }
}

if (!function_exists('intranet_config_env_bool')) {
    function intranet_config_env_bool(string $name, bool $default): bool
    {
        $value = intranet_config_env_value($name, '');
        if ($value === '') {
            return $default;
        }

        $normalized = strtolower(trim($value));
        if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
            return true;
        }
        if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
            return false;
        }

        return $default;
    }
}

if (!function_exists('intranet_config_define')) {
    function intranet_config_define(string $name, $value): void
    {
        if (!defined($name)) {
            define($name, $value);
        }
    }
}

if (!function_exists('intranet_config_constant_string')) {
    function intranet_config_constant_string(string $name, string $default = ''): string
    {
        if (!defined($name)) {
            return $default;
        }

        return trim((string) constant($name));
    }
}
PHP;

  $content .= "\nintranet_config_define('LDAP_SERVER', intranet_config_env_value('LDAP_SERVER', " . var_export($ldapServer, true) . "));\n";
  $content .= "intranet_config_define('LDAP_DOMAIN', intranet_config_env_value('LDAP_DOMAIN', " . var_export($ldapDomain, true) . "));\n";
  $content .= "intranet_config_define('LDAP_BASE_DN', intranet_config_env_value('LDAP_BASE_DN', " . var_export($ldapBaseDn, true) . "));\n";
  $content .= "intranet_config_define('LDAP_ADMIN_DN', intranet_config_env_value('LDAP_ADMIN_DN', " . var_export($ldapAdminDn, true) . "));\n";
  $content .= "intranet_config_define('LDAP_ADMIN_PASS', intranet_config_env_value('LDAP_ADMIN_PASS', ''));\n\n";
  $content .= "intranet_config_define('INTRANET_SITE_NAME', intranet_config_env_value('INTRANET_SITE_NAME', " . var_export($siteName, true) . "));\n";
  $content .= "intranet_config_define('INTRANET_ORGANIZATION_NAME', intranet_config_env_value('INTRANET_ORGANIZATION_NAME', " . var_export($organizationName, true) . "));\n";
  $content .= "intranet_config_define('INTRANET_TERRITORY_NAME', intranet_config_env_value('INTRANET_TERRITORY_NAME', " . var_export($territoryName, true) . "));\n";
  $content .= "intranet_config_define('INTRANET_UNIT_NAME', intranet_config_env_value('INTRANET_UNIT_NAME', " . var_export($unitName, true) . "));\n";
  $content .= "intranet_config_define('INTRANET_WEBSITE_URL', intranet_config_env_value('INTRANET_WEBSITE_URL', " . var_export($websiteUrl, true) . "));\n";
  $content .= "intranet_config_define('INTRANET_WELCOME_ENTITY', intranet_config_env_value('INTRANET_WELCOME_ENTITY', " . var_export($welcomeEntity, true) . "));\n";
  $content .= "intranet_config_define('APP_BASE_PATH', " . var_export($appBasePath, true) . ");\n\n";
  $content .= "intranet_config_define('DB_HOST', intranet_config_env_value('DB_HOST', " . var_export($dbHost, true) . "));\n";
  $content .= "intranet_config_define('DB_PORT', (int) intranet_config_env_value('DB_PORT', " . var_export((string)$dbPort, true) . "));\n";
  $content .= "intranet_config_define('DB_NAME', intranet_config_env_value('DB_NAME', " . var_export($dbName, true) . "));\n";
  $content .= "intranet_config_define('DB_USER', intranet_config_env_value('DB_USER', " . var_export($dbUser, true) . "));\n";
  $content .= "intranet_config_define('DB_PASS', intranet_config_env_value('DB_PASS', ''));\n";
  $content .= "intranet_config_define('CORE_UPDATE_BUILD_ENABLED', intranet_config_env_bool('CORE_UPDATE_BUILD_ENABLED', true));\n";
  $content .= "intranet_config_define('PLUGIN_STORE_PUBLISH_URL', intranet_config_env_value('PLUGIN_STORE_PUBLISH_URL', 'https://sede.csifsevilla.es/plugin-store/api/publish-core-update.php'));\n";
  $content .= "\n";
  $content .= "if (!function_exists('intranet_site_name')) {\n";
  $content .= "    function intranet_site_name(): string\n";
  $content .= "    {\n";
  $content .= "        return intranet_config_constant_string('INTRANET_SITE_NAME', 'Intranet');\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_organization_name')) {\n";
  $content .= "    function intranet_organization_name(): string\n";
  $content .= "    {\n";
  $content .= "        return intranet_config_constant_string('INTRANET_ORGANIZATION_NAME', 'Intranet');\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_territory_name')) {\n";
  $content .= "    function intranet_territory_name(): string\n";
  $content .= "    {\n";
  $content .= "        return intranet_config_constant_string('INTRANET_TERRITORY_NAME', '');\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_unit_name')) {\n";
  $content .= "    function intranet_unit_name(): string\n";
  $content .= "    {\n";
  $content .= "        \$unit = intranet_config_constant_string('INTRANET_UNIT_NAME', '');\n";
  $content .= "        if (\$unit !== '') {\n";
  $content .= "            return \$unit;\n";
  $content .= "        }\n";
  $content .= "        return intranet_site_name();\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_brand_name')) {\n";
  $content .= "    function intranet_brand_name(): string\n";
  $content .= "    {\n";
  $content .= "        \$organization = intranet_organization_name();\n";
  $content .= "        \$territory = intranet_territory_name();\n";
  $content .= "        \$brand = trim(\$organization . (\$territory !== '' ? ' ' . \$territory : ''));\n";
  $content .= "        if (\$brand !== '') {\n";
  $content .= "            return \$brand;\n";
  $content .= "        }\n";
  $content .= "        return intranet_site_name();\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_brand_name_upper')) {\n";
  $content .= "    function intranet_brand_name_upper(): string\n";
  $content .= "    {\n";
  $content .= "        \$brand = intranet_brand_name();\n";
  $content .= "        if (function_exists('mb_strtoupper')) {\n";
  $content .= "            return mb_strtoupper(\$brand, 'UTF-8');\n";
  $content .= "        }\n";
  $content .= "        return strtoupper(\$brand);\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_website_url')) {\n";
  $content .= "    function intranet_website_url(): string\n";
  $content .= "    {\n";
  $content .= "        return intranet_config_constant_string('INTRANET_WEBSITE_URL', '');\n";
  $content .= "    }\n";
  $content .= "}\n\n";
  $content .= "if (!function_exists('intranet_safe_slug')) {\n";
  $content .= "    function intranet_safe_slug(string \$text): string\n";
  $content .= "    {\n";
  $content .= "        \$text = trim(\$text);\n";
  $content .= "        if (\$text === '') {\n";
  $content .= "            return 'intranet';\n";
  $content .= "        }\n";
  $content .= "        if (function_exists('iconv')) {\n";
  $content .= "            \$converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', \$text);\n";
  $content .= "            if (is_string(\$converted) && \$converted !== '') {\n";
  $content .= "                \$text = \$converted;\n";
  $content .= "            }\n";
  $content .= "        }\n";
  $content .= "        \$text = strtolower(\$text);\n";
  $content .= "        \$text = preg_replace('/[^a-z0-9]+/', '_', \$text) ?? '';\n";
  $content .= "        \$text = trim(\$text, '_');\n";
  $content .= "        return \$text !== '' ? \$text : 'intranet';\n";
  $content .= "    }\n";
  $content .= "}\n";

  foreach ($preservedDefines as $constName => $rawValue) {
    $content .= "define('" . $constName . "', " . $rawValue . ");\n";
  }

  $content .= "?>\n";

  if (file_put_contents($path, $content) === false) {
    throw new RuntimeException("No se pudo escribir {$path}. Revisa permisos.");
  }

  return $content;
}
function secure_file_permissions(string $path, array &$notices, array &$errors): void
{
  $ok = @chmod($path, 0600);
  if ($ok) {
    $notices[] = "Permisos aplicados a {$path}: 600";
    return;
  }

  $ok2 = @chmod($path, 0640);
  if ($ok2) {
    $notices[] = "Permisos aplicados a {$path}: 640";
    return;
  }

  $errors[] = "Aviso: no se pudieron ajustar permisos de {$path}. Ajusta manualmente: chmod 600 {$path} o chmod 640 {$path}.";
}

function run_sql_file_safe(PDO $pdo, string $path, array &$notices, array &$errors, string $label): void
{
  if (!is_file($path)) {
    throw new RuntimeException("No existe el fichero SQL: {$path}");
  }

  $sql = file_get_contents($path);
  if ($sql === false) {
    throw new RuntimeException("No se pudo leer el fichero SQL: {$path}");
  }

  $sql = preg_replace('/^\xEF\xBB\xBF/', '', $sql);
  $sql = preg_replace('~/\*.*?\*/~s', '', $sql);

  $lines = preg_split("/\r\n|\n|\r/", $sql);
  $filtered = [];
  $skipped = 0;

  foreach ($lines as $line) {
    $t = trim($line);
    if ($t === '') {
      continue;
    }

    if (strpos($t, '--') === 0 || strpos($t, '#') === 0) {
      continue;
    }

    if (
      preg_match('/\bSET\s+(?:GLOBAL|@@GLOBAL|SESSION|@@SESSION)\b/i', $t) ||
      preg_match('/\bsql_log_bin\b/i', $t) ||
      preg_match('/\bDEFINER\s*=\s*/i', $t) ||
      preg_match('/\bCREATE\s+DEFINER\b/i', $t) ||
      preg_match('/\bCREATE\s+EVENT\b/i', $t) ||
      preg_match('/\bSET\s+USER\b/i', $t)
    ) {
      $skipped++;
      continue;
    }

    $filtered[] = $line;
  }

  $sql2 = implode("\n", $filtered);
  $stmts = array_filter(array_map('trim', explode(';', $sql2)), fn($s) => $s !== '');

  foreach ($stmts as $s) {
    try {
      $pdo->exec($s);
    } catch (Throwable $e) {
      $m = $e->getMessage();

      if (
        strpos($m, '42S01') !== false ||
        strpos($m, ' 1050 ') !== false ||
        stripos($m, 'already exists') !== false
      ) {
        continue;
      }

      if (
        strpos($m, '23000') !== false &&
        stripos($m, 'duplicate') !== false
      ) {
        continue;
      }

      if (
        strpos($m, '42000') !== false &&
        (
          strpos($m, ' 1068 ') !== false ||
          stripos($m, 'Multiple primary key defined') !== false
        )
      ) {
        continue;
      }

      if (
        strpos($m, '42000') !== false &&
        (
          strpos($m, ' 1061 ') !== false ||
          stripos($m, 'Duplicate key name') !== false
        )
      ) {
        continue;
      }

      if (is_1227($e)) {
        $errors[] = "{$label}: se ha omitido parte del SQL por requerir SUPER/SET USER (1227). Detalle: " . $e->getMessage();
        continue;
      }

      throw $e;
    }
  }

  if ($skipped > 0) {
    $notices[] = "{$label}: se han omitido {$skipped} linea(s) potencialmente problematicas.";
  }
  $notices[] = "{$label}: ejecutado correctamente.";
}

$errors = [];
$notices = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $action = $_POST['action'] ?? '';

  if ($action === 'save_step1') {
    $_SESSION['site_name'] = trim($_POST['site_name'] ?? 'Intranet');
    $_SESSION['organization_name'] = trim($_POST['organization_name'] ?? 'CSIF');
    $_SESSION['territory_name'] = trim($_POST['territory_name'] ?? 'Sevilla');
    $_SESSION['unit_name'] = trim($_POST['unit_name'] ?? 'Unión Provincial de Sevilla');
    $_SESSION['website_url'] = normalize_external_url($_POST['website_url'] ?? '');
    $_SESSION['welcome_entity'] = trim($_POST['welcome_entity'] ?? 'la organizacion');
    $_SESSION['primary_color'] = normalize_hex_color($_POST['primary_color'] ?? '#067B3B');
    $_SESSION['app_base_path'] = normalize_base_path($_POST['app_base_path'] ?? '');
    header("Location: index.php?step=2");
    exit;
  }

  if ($action === 'save_step2') {
    $_SESSION['db'] = [
      'host' => trim($_POST['db_host'] ?? 'localhost'),
      'port' => (int) ($_POST['db_port'] ?? 3306),
      'name' => trim($_POST['db_name'] ?? ''),
      'user' => trim($_POST['db_user'] ?? ''),
      'pass' => (string) ($_POST['db_pass'] ?? ''),
      'user_host' => trim($_POST['db_user_host'] ?? 'localhost'),
    ];

    $_SESSION['create_db'] = isset($_POST['create_db']);

    $_SESSION['admin'] = [
      'host' => trim($_POST['db_host'] ?? 'localhost'),
      'port' => (int) ($_POST['db_port'] ?? 3306),
      'user' => trim($_POST['admin_user'] ?? ''),
      'pass' => (string) ($_POST['admin_pass'] ?? ''),
    ];

    if (isset($_POST['test_db'])) {
      $db = $_SESSION['db'];
      $admin = $_SESSION['admin'] ?? [];
      $wantCreate = !empty($_SESSION['create_db']);

      if ($wantCreate) {
        if (empty($admin['user'])) {
          $errors[] = "Has marcado 'Crear BD y usuario' pero no has indicado el usuario admin MySQL.";
        } else {
          try {
            $pdoAdmin = pdo_admin_connect($admin);
            $notices[] = "Conexion BD (usuario admin): OK";
            test_admin_privileges($pdoAdmin, $db, $admin, $notices, $errors);
          } catch (Throwable $e) {
            $errors[] = "No se pudo conectar con el usuario admin MySQL: " . $e->getMessage();
          }
        }
      }

      try {
        $pdo = pdo_connect($db);
        $pdo->query("SELECT 1");
        $notices[] = "Conexion BD (usuario aplicacion): OK";
      } catch (Throwable $e) {
        if ($wantCreate) {
          $notices[] = "Usuario de aplicacion aun no disponible (se creara en la instalacion): " . $e->getMessage();
        } else {
          $errors[] = "No se pudo conectar a la BD con el usuario de aplicacion: " . $e->getMessage();
        }
      }
    } else {
      header("Location: index.php?step=3");
      exit;
    }
  }

  if ($action === 'save_step3') {
    $ldapMode = strtolower(trim((string)($_POST['ldap_mode'] ?? 'ldap')));
    if (!in_array($ldapMode, ['ldap', 'ldaps'], true)) {
      $ldapMode = 'ldap';
    }
    $_SESSION['ldap_mode'] = $ldapMode;

    $existingLdap = $_SESSION['ldap'] ?? [];
    $existingServer = trim((string)($existingLdap['server'] ?? ''));
    if ($existingServer === '' || !preg_match('#^ldaps?://#i', $existingServer)) {
      $existingServer = ($ldapMode === 'ldaps' ? 'ldaps://' : 'ldap://');
    } else {
      $existingServer = preg_replace('#^ldaps?://#i', $ldapMode . '://', $existingServer);
    }
    $existingLdap['server'] = $existingServer;
    $_SESSION['ldap'] = $existingLdap;

    $nextStep = $ldapMode === 'ldaps' ? 4 : 5;
    header("Location: index.php?step={$nextStep}");
    exit;
  }

  if ($action === 'save_step4') {
    $ldapMode = (string)($_SESSION['ldap_mode'] ?? 'ldap');
    if ($ldapMode !== 'ldaps') {
      header("Location: index.php?step=5");
      exit;
    }

    if (isset($_POST['manual_cert_done'])) {
      $_SESSION['ldaps_ca_installed'] = [
        'path' => '/usr/local/share/ca-certificates/' . ldaps_manual_cert_filename(),
        'uploaded_name' => basename((string)(($_SESSION['ldaps_ca_pending']['uploaded_name'] ?? 'intranet-ldaps-ca.crt'))),
        'installed_at' => date('c'),
        'log' => 'Instalacion manual confirmada por el administrador.',
        'manual' => true,
      ];
      $notices[] = 'Se ha marcado el certificado como instalado manualmente. En el siguiente paso podras probar LDAPS.';
    } elseif (!empty($_FILES['ldaps_ca_cert']['name'] ?? '')) {
      try {
        [$cachedPath, $cachedPem] = cache_uploaded_ca_certificate($_FILES['ldaps_ca_cert'], 'intranet-ldaps-ca.crt');
        $_SESSION['ldaps_ca_pending'] = [
          'temp_path' => $cachedPath,
          'uploaded_name' => basename((string)($_FILES['ldaps_ca_cert']['name'] ?? 'certificado')),
          'cached_at' => date('c'),
        ];

        [$installedPath, $installLog] = install_uploaded_ca_certificate($_FILES['ldaps_ca_cert'], 'intranet-ldaps-ca.crt');
        $_SESSION['ldaps_ca_installed'] = [
          'path' => $installedPath,
          'uploaded_name' => basename((string)($_FILES['ldaps_ca_cert']['name'] ?? 'certificado')),
          'installed_at' => date('c'),
          'log' => $installLog,
        ];
        $notices[] = "Certificado de CA instalado correctamente en {$installedPath}.";
      } catch (Throwable $e) {
        unset($_SESSION['ldaps_ca_installed']);
        $errors[] = "No se pudo instalar el certificado LDAPS automaticamente: " . $e->getMessage();
        $errors[] = 'Puedes instalarlo a mano con los comandos indicados debajo y luego pulsar "Instalar manualmente y continuar".';
        $step = 4;
      }
    } elseif (empty($_SESSION['ldaps_ca_installed'])) {
      $errors[] = 'Debes subir el certificado de la CA para continuar con LDAPS.';
      $step = 4;
    }

    if (empty($errors)) {
      header("Location: index.php?step=5");
      exit;
    }
  }

  if ($action === 'save_step5') {
    $ldapMode = (string)($_SESSION['ldap_mode'] ?? 'ldap');
    $defaultScheme = $ldapMode === 'ldaps' ? 'ldaps://' : 'ldap://';
    $server = trim((string)($_POST['ldap_server'] ?? ''));
    if ($server === '') {
      $server = $defaultScheme;
    } elseif (!preg_match('#^ldaps?://#i', $server)) {
      $server = $defaultScheme . ltrim($server, '/');
    } else {
      $server = preg_replace('#^ldaps?://#i', $ldapMode . '://', $server);
    }

    $_SESSION['ldap'] = [
      'server' => $server,
      'domain' => trim((string)($_POST['ldap_domain'] ?? '')),
      'base_dn' => trim((string)($_POST['ldap_base_dn'] ?? '')),
      'bind_dn' => trim((string)($_POST['ldap_bind_dn'] ?? '')),
      'bind_pass' => (string) ($_POST['ldap_bind_pass'] ?? ''),
    ];

    if ($ldapMode === 'ldaps' && empty($_SESSION['ldaps_ca_installed'])) {
      $errors[] = 'Antes de probar o guardar LDAPS debes instalar el certificado de la CA.';
      $step = 4;
    } elseif (isset($_POST['test_ldap'])) {
      [$ok, $msg] = ldap_test($_SESSION['ldap']);
      if ($ok) {
        $notices[] = "LDAP: OK";
      } else {
        $errors[] = "LDAP: $msg";
      }
      $step = 5;
    } else {
      header("Location: index.php?step=6");
      exit;
    }
  }

  if ($action === 'do_install') {
    install_progress_start();
    install_progress_line("Iniciando instalacion...");

    try {
      $db = $_SESSION['db'] ?? null;
      $ldap = $_SESSION['ldap'] ?? null;
      $branding = [
        'site_name' => $_SESSION['site_name'] ?? 'Intranet',
        'organization_name' => $_SESSION['organization_name'] ?? 'CSIF',
        'territory_name' => $_SESSION['territory_name'] ?? 'Sevilla',
        'unit_name' => $_SESSION['unit_name'] ?? 'Unión Provincial de Sevilla',
        'website_url' => $_SESSION['website_url'] ?? '',
        'welcome_entity' => $_SESSION['welcome_entity'] ?? 'la organizacion',
      ];
      $primaryColor = $_SESSION['primary_color'] ?? '#067B3B';
      $appBasePath = $_SESSION['app_base_path'] ?? '';

      if (!$db || !$ldap) {
        throw new RuntimeException("Faltan datos. Vuelve atras.");
      }
      if (ldap_is_ldaps((string)($ldap['server'] ?? '')) && empty($_SESSION['ldaps_ca_installed'])) {
        throw new RuntimeException("Antes de instalar debes completar el paso intermedio de certificado LDAPS.");
      }

      install_progress_line("Guardando logos...");
      save_logo($_FILES['logo_color'] ?? [], dirname(__DIR__) . '/styles/images/logo.png');
      save_logo($_FILES['logo_white'] ?? [], dirname(__DIR__) . '/styles/images/logob.png');

      install_progress_line("Generando paleta de colores...");
      $themeGenerated = write_theme_vars_css($primaryColor);
      install_progress_line("Paleta de colores generada.", "#86efac");

      $admin = $_SESSION['admin'] ?? null;
      $wantCreate = !empty($_SESSION['create_db']);
      $hasAdminCreds = is_array($admin) && !empty($admin['user']);

      if ($wantCreate) {
        install_progress_line("Creando BD/usuario/permisos...");
        if (!$hasAdminCreds) {
          throw new RuntimeException("Faltan credenciales admin MySQL para crear BD/usuario.");
        }
        try {
          ensure_db_and_user($admin, $db, $db['user_host']);
          install_progress_line("Creacion BD/usuario/permisos: OK", "#86efac");
        } catch (Throwable $e) {
          if (is_1227($e)) {
            install_progress_line("AVISO: el admin no puede ejecutar alguna operacion que requiere SUPER/SET USER (1227).", "#fde68a");
            install_progress_line("Detalle: " . $e->getMessage(), "#fde68a");
            install_progress_line("Continuare. El usuario/permisos puede quedar pendiente.", "#fde68a");
          } else {
            throw $e;
          }
        }
      } else {
        install_progress_line("No se solicito crear BD/usuario. Se usara el usuario de aplicacion.");
      }

      install_progress_line("Conectando a la BD...");
      try {
        $pdo = pdo_connect($db);
        install_progress_line("Conexion con usuario de aplicacion: OK", "#86efac");
      } catch (Throwable $e) {
        if ($wantCreate && $hasAdminCreds) {
          $dsnAdmin = "mysql:host={$admin['host']};port={$admin['port']};dbname={$db['name']};charset=utf8mb4";
          $pdo = new PDO($dsnAdmin, $admin['user'], $admin['pass'], [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          ]);
          install_progress_line("Conexion como ADMIN para crear tablas: OK", "#86efac");
        } else {
          throw new RuntimeException("No se pudo conectar con el usuario de aplicacion. Detalle: " . $e->getMessage());
        }
      }

      install_progress_line("Importando schema (tablas)...");
      run_sql_file_safe($pdo, dirname(__DIR__) . '/config/intranet_schema.sql', $notices, $errors, 'Schema (tablas)');
      install_progress_line("Schema importado.", "#86efac");

      install_progress_line("Importando seed (datos)...");
      run_sql_file_safe($pdo, dirname(__DIR__) . '/config/intranet_seed.sql', $notices, $errors, 'Seed (datos)');
      install_progress_line("Seed importado.", "#86efac");

      install_progress_line("Escribiendo common/config.php...");
      $generated = write_common_config_php($ldap, $db, $branding, $appBasePath);
      $cfgPath = dirname(__DIR__) . '/common/config.php';
      secure_file_permissions($cfgPath, $notices, $errors);
      install_progress_line("common/config.php actualizado.", "#86efac");
      install_progress_line("Escribiendo config/secrets.local.php...");
      write_secret_config_php($ldap, $db);
      $secretCfgPath = dirname(__DIR__) . '/config/secrets.local.php';
      secure_file_permissions($secretCfgPath, $notices, $errors);
      install_progress_line("config/secrets.local.php actualizado.", "#86efac");

      install_progress_line("Finalizando...");
      file_put_contents($lock, 'installed ' . date('c'));
      install_progress_line("Instalacion terminada.", "#86efac");

      echo "<hr>";
      echo "<h3>common/config.php generado</h3>";
      echo "<pre style='white-space:pre-wrap; background:#0b1220; color:#e5e7eb; padding:12px; border-radius:10px; overflow:auto; max-height:420px;'>" . h($generated) . "</pre>";
      echo "<h3>styles/css/theme-vars.css generado</h3>";
      echo "<pre style='white-space:pre-wrap; background:#0b1220; color:#e5e7eb; padding:12px; border-radius:10px; overflow:auto; max-height:220px;'>" . h($themeGenerated) . "</pre>";
      echo "<h3>Configuracion adicional de Apache</h3>";
      echo "<pre style='white-space:pre-wrap; background:#0b1220; color:#e5e7eb; padding:12px; border-radius:10px; overflow:auto; max-height:260px;'>"
        . h('SetEnv SMTP_HOST "tu_servidor_smtp"' . "\n"
          . 'SetEnv SMTP_USER "tu_email"' . "\n"
          . 'SetEnv SMTP_PASS "tu_password"' . "\n"
          . 'SetEnv SMTP_PORT "tu_puerto_smtp"' . "\n\n"
          . 'SetEnv MAIL_NOREPLY_EMAIL "tu_email_de_no_respuesta"' . "\n"
          . 'SetEnv MAIL_NOREPLY_NAME  "tu_nombre_de_no_respuesta"')
        . "</pre>";

      echo "<div style='display:flex;gap:10px;flex-wrap:wrap;margin-top:12px'>";
      echo "<a class='btn primary' href='../login.php'>Ir al login</a>";
      echo "<a class='btn' href='index.php'>Volver al instalador</a>";
      echo "</div>";
      echo "</div></div></body></html>";
      exit;

    } catch (Throwable $e) {
      install_progress_line("ERROR: " . $e->getMessage(), "#fca5a5");
      $backStep = (string)($_SESSION['ldap_mode'] ?? 'ldap') === 'ldaps' ? 6 : 5;
      echo "<hr><a class='btn' href='index.php?step=" . (int)$backStep . "'>Volver</a>";
      echo "</div></div></body></html>";
      exit;
    }
  }

  if ($action === 'delete_installer') {
    $ok = rrmdir(__DIR__);
    if ($ok) {
      echo "<!doctype html><meta charset='utf-8'><p>Instalador eliminado. <a href='../login.php'>Ir al login</a></p>";
      exit;
    }
    $errors[] = 'No se pudo eliminar el instalador por permisos. Borra la carpeta /instalacion manualmente.';
    $step = 6;
  }
}
?>
<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Instalacion | Intranet</title>
  <link rel="stylesheet" href="assets/style.css">
</head>
<body>
  <div class="wrap">
    <div class="header">
      <img src="../styles/images/logo.png" alt="logo" onerror="this.style.display='none'">
      <div>
        <h1>Instalacion de la Intranet</h1>
        <div class="steps">
          <?= step_badge(1, 'Sitio', $step) ?>
          <?= step_badge(2, 'Base de datos', $step) ?>
          <?= step_badge(3, 'Tipo LDAP', $step) ?>
          <?php if ($installUsesLdaps): ?>
            <?= step_badge(4, 'Certificado LDAPS', $step) ?>
            <?= step_badge(5, 'Configuracion LDAP', $step) ?>
            <?= step_badge(6, 'Logos e instalar', $step) ?>
          <?php else: ?>
            <?= step_badge(5, 'Configuracion LDAP', $step) ?>
            <?= step_badge(6, 'Logos e instalar', $step) ?>
          <?php endif; ?>
        </div>
      </div>
    </div>

    <div class="card">
      <?php foreach ($errors as $e): ?>
        <div class="notice bad"><?= h($e) ?></div>
      <?php endforeach; ?>
      <?php foreach ($notices as $n): ?>
        <div class="notice"><?= h($n) ?></div>
      <?php endforeach; ?>

      <?php if ($step === 1):
        $req = check_requirements();
        $site = $_SESSION['site_name'] ?? 'Intranet CSIF Sevilla';
        $organizationName = $_SESSION['organization_name'] ?? 'CSIF';
        $territoryName = $_SESSION['territory_name'] ?? 'Sevilla';
        $unitName = $_SESSION['unit_name'] ?? 'Unión Provincial de Sevilla';
        $websiteUrl = $_SESSION['website_url'] ?? 'https://www.csif.es/sevilla';
        $welcomeEntity = $_SESSION['welcome_entity'] ?? 'la Unión Provincial de Sevilla de CSIF';
        $primaryColor = $_SESSION['primary_color'] ?? '#067B3B';
        $appBasePath = $_SESSION['app_base_path'] ?? '';
      ?>
        <h2>Paso 1: Comprobaciones y datos generales</h2>
        <?php foreach ($req as [$label, $ok, $extra]): ?>
          <div class="kv"><b><?= h($label) ?></b><div><?= $ok ? 'OK' : 'Falta' ?> <?= h($extra) ?></div></div>
        <?php endforeach; ?>
        <hr>
        <form method="post">
          <input type="hidden" name="action" value="save_step1">

          <label>Nombre de la sede</label>
          <input type="text" name="site_name" value="<?= h($site) ?>" placeholder="Intranet CSIF Cadiz">

          <label>Nombre del organismo</label>
          <input type="text" name="organization_name" value="<?= h($organizationName) ?>" placeholder="CSIF">

          <label>Provincia o ambito territorial</label>
          <input type="text" name="territory_name" value="<?= h($territoryName) ?>" placeholder="Cadiz">

          <label>Nombre de la sede o unidad</label>
          <input type="text" name="unit_name" value="<?= h($unitName) ?>" placeholder="Unión Provincial de Cádiz">

          <label>Web publica de referencia</label>
          <input type="text" name="website_url" value="<?= h($websiteUrl) ?>" placeholder="https://www.organizacion.es/cadiz">

          <label>Para quien se instala la intranet?</label>
          <input type="text" name="welcome_entity" value="<?= h($welcomeEntity) ?>" placeholder="la Unión Provincial de Cádiz de CSIF">
          <p><small>Se usara en el mensaje de bienvenida del portal.</small></p>

          <label>Ruta base relativa de la intranet</label>
          <input type="text" name="app_base_path" value="<?= h($appBasePath) ?>" placeholder="/apps/intranet2.0">
          <p><small>Dejalo vacio si la intranet va en la raiz del sitio. Ejemplos validos: <code>/intranet</code>, <code>/apps/intranet2.0</code>.</small></p>

          <label style="margin-top:12px;">Color principal de la intranet</label>
          <div style="display:flex; gap:10px; align-items:center; flex-wrap:wrap;">
            <input type="color" id="install_primary_color" name="primary_color" value="<?= h($primaryColor) ?>" style="width:70px; height:44px; padding:4px;">
            <input type="text" id="install_primary_color_text" value="<?= h($primaryColor) ?>" readonly style="max-width:140px;">
          </div>
          <p><small>El instalador generara automaticamente <code>--primary</code>, <code>--primary-dark</code>, <code>--primary-light</code> y <code>--accent</code>.</small></p>

          <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
            <button class="btn primary" type="submit">Continuar</button>
          </div>
        </form>

      <?php elseif ($step === 2):
        $db = $_SESSION['db'] ?? ['host' => 'localhost', 'port' => 3306, 'name' => 'intranet', 'user' => 'webuser', 'pass' => '', 'user_host' => 'localhost'];
        $create = $_SESSION['create_db'] ?? false;
      ?>
        <h2>Paso 2: Base de datos</h2>
        <form method="post">
          <input type="hidden" name="action" value="save_step2">
          <div class="grid">
            <div><label>Host</label><input type="text" name="db_host" value="<?= h($db['host']) ?>"></div>
            <div><label>Puerto</label><input type="number" name="db_port" value="<?= h($db['port']) ?>"></div>
            <div><label>Nombre de base de datos</label><input type="text" name="db_name" value="<?= h($db['name']) ?>"></div>
            <div><label>Usuario aplicacion</label><input type="text" name="db_user" value="<?= h($db['user']) ?>"></div>
            <div><label>Password usuario aplicacion</label><input type="password" name="db_pass" value="<?= h($db['pass']) ?>"></div>
            <div><label>Host del usuario (MySQL)</label><input type="text" name="db_user_host" value="<?= h($db['user_host']) ?>"></div>
          </div>
          <hr>
          <label><input type="checkbox" name="create_db" <?= $create ? 'checked' : '' ?>> Crear BD y usuario y asignar permisos</label>
          <div class="grid" style="margin-top:8px">
            <div><label>Usuario admin MySQL</label><input type="text" name="admin_user" value="<?= h($_SESSION['admin']['user'] ?? '') ?>"></div>
            <div><label>Password admin MySQL</label><input type="password" name="admin_pass" value="<?= h($_SESSION['admin']['pass'] ?? '') ?>"></div>
          </div>
          <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
            <button class="btn" name="test_db" value="1" type="submit">Probar conexion</button>
            <a class="btn" href="index.php?step=1">Atras</a>
            <button class="btn primary" type="submit">Continuar</button>
          </div>
        </form>

      <?php elseif ($step === 3):
        $ldapMode = (string)($_SESSION['ldap_mode'] ?? (($installUsesLdaps) ? 'ldaps' : 'ldap'));
      ?>
        <h2>Paso 3: Tipo de conexion LDAP</h2>
        <form method="post">
          <input type="hidden" name="action" value="save_step3">
          <label>Modo de conexion</label>
          <select name="ldap_mode">
            <option value="ldap" <?= $ldapMode === 'ldap' ? 'selected' : '' ?>>LDAP (sin cifrado)</option>
            <option value="ldaps" <?= $ldapMode === 'ldaps' ? 'selected' : '' ?>>LDAPS (seguro, requiere certificado de CA)</option>
          </select>
          <div class="notice" style="margin-top:12px;">
            <b>Recomendacion:</b> usa <b>LDAPS</b> cuando el servidor Linux pueda confiar en la CA del dominio. Si eliges LDAPS, el instalador te pedira el certificado antes de la configuracion final del directorio.
          </div>
          <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
            <a class="btn" href="index.php?step=2">Atras</a>
            <button class="btn primary" type="submit">Continuar</button>
          </div>
        </form>

      <?php elseif ($step === 4 && $installUsesLdaps): ?>
        <?php $ldap = $_SESSION['ldap'] ?? ['server' => '']; $ldapsInstalled = $_SESSION['ldaps_ca_installed'] ?? null; $ldapsPending = $_SESSION['ldaps_ca_pending'] ?? null; ?>
        <h2>Paso 4: Certificado LDAPS</h2>

        <div class='notice'>
          <b>Has elegido LDAPS.</b> Antes de continuar, este servidor Linux debe confiar en la <b>CA</b> que firmo el certificado del controlador de dominio.
        </div>

        <div class='notice'>
          <b>Como exportarlo desde Windows Server:</b>
          <ol style='margin:10px 0 0 18px;'>
            <li>Abre <code>mmc</code>.</li>
            <li>Ve a <b>Archivo -> Agregar o quitar complemento</b>.</li>
            <li>Anade <b>Certificados</b> para <b>Cuenta de equipo -> Equipo local</b>.</li>
            <li>Entra en <b>Entidades de certificacion raiz de confianza -> Certificados</b>.</li>
            <li>Busca la CA del dominio y exportala <b>sin clave privada</b> en formato <b>Base-64 encoded X.509 (.CER)</b>.</li>
          </ol>
        </div>

        <?php if ($ldapsInstalled): ?>
          <div class='notice'>
            <b>Certificado preparado:</b> <?= h($ldapsInstalled['uploaded_name'] ?? 'certificado') ?><br>
            <small>Ruta objetivo: <?= h($ldapsInstalled['path'] ?? '') ?></small>
            <?php if (!empty($ldapsInstalled['manual'])): ?>
              <br><small>La instalacion se marco como manual. Usa el boton "Probar LDAPS" del paso siguiente para verificarlo.</small>
            <?php endif; ?>
          </div>
        <?php endif; ?>

        <?php
          $showManualHelp = !empty($ldapsPending) || !empty($ldapsInstalled['manual']);
        ?>

        <form method='post' enctype='multipart/form-data'>
          <input type='hidden' name='action' value='save_step4'>
          <label>Certificado de la CA (.cer, .crt o .pem)</label>
          <input type='file' name='ldaps_ca_cert' accept='.cer,.crt,.pem,application/x-x509-ca-cert' <?= empty($ldapsInstalled) ? 'required' : '' ?>>
          <p><small>El instalador intentara copiarlo a <code>/usr/local/share/ca-certificates/</code> y ejecutar <code>update-ca-certificates</code>.</small></p>

          <div style='margin-top:14px;display:flex;gap:10px;flex-wrap:wrap'>
            <a class='btn' href='index.php?step=3'>Atras</a>
            <button class='btn primary' type='submit'>Instalar certificado y continuar</button>
          </div>
        </form>

        <?php if ($showManualHelp): ?>
          <div class='notice' style='margin-top:16px;'>
            <b>Si la instalacion automatica falla por permisos, hazlo a mano desde Linux:</b>
            <ol style='margin:10px 0 0 18px;'>
              <li>Conectate por SSH al servidor Linux.</li>
              <li>Ejecuta estos comandos con un usuario con <code>sudo</code>:</li>
            </ol>
            <pre style='white-space:pre-wrap; margin-top:10px;'><?= h(implode("
", ldaps_manual_install_commands())) ?></pre>
            <p style='margin-top:10px;'><small>El instalador ha dejado una copia temporal del certificado en: <code><?= h((string)($ldapsPending['temp_path'] ?? (__DIR__ . '/tmp/' . ldaps_manual_cert_filename()))) ?></code></small></p>
            <p style='margin-top:10px;'><small>Cuando termines los comandos en Linux, pulsa este boton para continuar al siguiente paso sin volver a subir el certificado.</small></p>
            <form method='post' style='margin-top:12px;'>
              <input type='hidden' name='action' value='save_step4'>
              <input type='hidden' name='manual_cert_done' value='1'>
              <button class='btn' type='submit'>Instalar manualmente y continuar</button>
            </form>
          </div>
        <?php endif; ?>

      <?php elseif ($step === 5):
        $ldapMode = (string)($_SESSION['ldap_mode'] ?? (($installUsesLdaps) ? 'ldaps' : 'ldap'));
        $defaultServer = $ldapMode === 'ldaps' ? 'ldaps://' : 'ldap://';
        $ldap = $_SESSION['ldap'] ?? ['server' => $defaultServer, 'domain' => '', 'base_dn' => '', 'bind_dn' => '', 'bind_pass' => ''];
        if (empty($ldap['server'])) {
          $ldap['server'] = $defaultServer;
        }
      ?>
        <h2>Paso 5: Configuracion <?= $ldapMode === 'ldaps' ? 'LDAPS' : 'LDAP' ?></h2>
        <?php if ($ldapMode === 'ldaps'): ?>
          <div class="notice">
            <b>LDAPS activado.</b> Usa preferentemente el <b>FQDN</b> del controlador de dominio en lugar de la IP, para que coincida con el certificado del servidor.
          </div>
        <?php endif; ?>
        <form method="post">
          <input type="hidden" name="action" value="save_step5">
          <div class="grid">
            <div><label>Servidor <?= $ldapMode === 'ldaps' ? 'LDAPS' : 'LDAP' ?></label><input type="text" name="ldap_server" value="<?= h($ldap['server']) ?>"></div>
            <div><label>Dominio</label><input type="text" name="ldap_domain" value="<?= h($ldap['domain']) ?>"></div>
            <div><label>Base DN</label><input type="text" name="ldap_base_dn" value="<?= h($ldap['base_dn']) ?>"></div>
            <div><label>Bind DN</label><input type="text" name="ldap_bind_dn" value="<?= h($ldap['bind_dn']) ?>"></div>
            <div><label>Password bind</label><input type="password" name="ldap_bind_pass" value="<?= h($ldap['bind_pass']) ?>"></div>
          </div>
          <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
            <button class="btn" name="test_ldap" value="1" type="submit">Probar <?= $ldapMode === 'ldaps' ? 'LDAPS' : 'LDAP' ?></button>
            <a class="btn" href="index.php?step=<?= $installUsesLdaps ? 4 : 3 ?>">Atras</a>
            <button class="btn primary" type="submit">Continuar</button>
          </div>
        </form>

      <?php elseif ($step === 6): ?>
        <h2>Paso 6: Logos e instalacion</h2>

        <div class="notice">
          <b>Importante:</b> para que funcione el correo saliente, en Apache debes definir:
          <pre style="white-space:pre-wrap; margin-top:10px;">SetEnv SMTP_HOST "tu_servidor_smtp"
SetEnv SMTP_USER "tu_email"
SetEnv SMTP_PASS "tu_password"
SetEnv SMTP_PORT "tu_puerto_smtp"

SetEnv MAIL_NOREPLY_EMAIL "tu_email_de_no_respuesta"
SetEnv MAIL_NOREPLY_NAME  "tu_nombre_de_no_respuesta"</pre>
        </div>

        <form method="post" enctype="multipart/form-data">
          <input type="hidden" name="action" value="do_install">
          <label>Logo a color (PNG)</label>
          <input id="logo_color" type="file" name="logo_color" accept="image/png" required>
          <div class="preview"><img id="prev_color" style="display:none" alt="preview color"></div>

          <label>Logo blanco (PNG)</label>
          <input id="logo_white" type="file" name="logo_white" accept="image/png" required>
          <div class="preview"><img id="prev_white" style="display:none" alt="preview blanco"></div>

          <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
            <a class="btn" href="index.php?step=5">Atras</a>
            <button class="btn primary" type="submit">Instalar</button>
          </div>
        </form>
        <script src="assets/install.js"></script>
      <?php else: ?>
        <h2>Finalizado</h2>
        <p>La instalacion ha terminado. Por seguridad, es recomendable borrar la carpeta <code>/instalacion</code>.</p>
        <div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:12px">
          <a class="btn primary" href="../login.php">Ir al login</a>
          <form method="post" style="margin:0">
            <input type="hidden" name="action" value="delete_installer">
            <button class="btn danger" type="submit">Eliminar instalador ahora</button>
          </form>
        </div>
      <?php endif; ?>
    </div>
  </div>
  <script src="assets/install.js"></script>
  <script>
    (function () {
      var colorInput = document.getElementById('install_primary_color');
      var colorText = document.getElementById('install_primary_color_text');
      if (!colorInput || !colorText) return;

      function syncColorPreview() {
        colorText.value = (colorInput.value || "").toUpperCase();
      }

      colorInput.addEventListener('input', syncColorPreview);
      colorInput.addEventListener('change', syncColorPreview);
      syncColorPreview();
    })();
  </script>
</body>
</html>
