#!/usr/bin/env php
<?php

declare(strict_types=1);

/**
 * Innovium · CLI
 *
 * Uso: php scripts/innovium <comando> [args...]
 *
 * Comandos disponibles:
 *   serve [host] [port]               (Fase 0)
 *   key:generate                      (Fase 0)
 *   make:migration <name> [master|tenant]   (Fase 0)
 *   migrate:tenant <slug>             (Sprint 1.1)
 *   db:create-demo                    (Sprint 1.1)
 *   db:seed-demo                      (Sprint 1.1)
 *   master:migrate                    (Sprint 1.2)
 *   tenant:create <slug>              (Sprint 1.2)
 *   db:seed-master                    (Sprint 1.2)
 *   db:create-infinia                 (Sprint 1.2)
 *   db:seed-infinia                   (Sprint 1.2)
 *   db:seed-catalogo <slug>           (Sprint 1.3)
 *   catalogos:seed <slug>             (Sprint 1.5a — catálogos chilenos retroactivos)
 *   help
 *
 * Comandos pendientes (Sprint 1.3+):
 *   db:fresh
 */

require __DIR__ . '/../bootstrap.php';

$argv    = $_SERVER['argv'] ?? [];
$command = $argv[1] ?? 'help';

$exitCode = 0;

switch ($command) {

    case 'serve':
        // Default 0.0.0.0 (no localhost) para que el built-in server PHP
        // acepte requests con cualquier Host header — necesario en multi-tenancy
        // local: los subdominios .innovium.test que apuntan a 127.0.0.1 vía
        // hosts file llegan con Host: <slug>.innovium.test, no Host: localhost.
        // Con bind a localhost, PHP rechazaría esos requests a nivel TCP.
        $host = $argv[2] ?? '0.0.0.0';
        $port = $argv[3] ?? '8000';
        $docroot = INNOVIUM_ROOT . '/public';

        echo "\n";
        echo "  Innovium · dev server\n";
        echo "  bind: {$host}:{$port}\n";
        echo "  URLs:\n";
        echo "    http://localhost:{$port}\n";
        echo "    http://demo.innovium.test:{$port}/login\n";
        echo "    http://infinia.innovium.test:{$port}/login\n";
        echo "  docroot: {$docroot}\n";
        echo "  Ctrl+C para detener.\n\n";

        passthru('php -S ' . escapeshellarg("{$host}:{$port}") . ' -t ' . escapeshellarg($docroot), $exitCode);
        break;

    case 'key:generate':
        $key = base64_encode(random_bytes(32));
        $envPath = INNOVIUM_ROOT . '/.env';

        if (!file_exists($envPath)) {
            fwrite(STDERR, "ERROR: no existe .env en " . INNOVIUM_ROOT . ". Copialo de .env.example.\n");
            exit(1);
        }

        $content = (string)file_get_contents($envPath);
        $replacement = "APP_KEY={$key}";

        if (preg_match('/^APP_KEY=.*$/m', $content) === 1) {
            $content = (string)preg_replace('/^APP_KEY=.*$/m', $replacement, $content);
        } else {
            $content = rtrim($content, "\n") . "\n{$replacement}\n";
        }

        if (file_put_contents($envPath, $content) === false) {
            fwrite(STDERR, "ERROR: no puedo escribir en {$envPath}\n");
            exit(1);
        }

        echo "APP_KEY generada y escrita a .env\n";
        echo "{$key}\n";
        break;

    case 'make:migration':
        if (!isset($argv[2])) {
            fwrite(STDERR, "Uso: php scripts/innovium make:migration <name> [master|tenant]\n");
            exit(1);
        }

        $name = (string)$argv[2];
        $type = $argv[3] ?? 'tenant';

        if (!in_array($type, ['master', 'tenant'], true)) {
            fwrite(STDERR, "ERROR: type debe ser 'master' o 'tenant', recibido '{$type}'.\n");
            exit(1);
        }

        $dir = INNOVIUM_ROOT . "/database/migrations/{$type}";
        if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
            fwrite(STDERR, "ERROR: no puedo crear {$dir}\n");
            exit(1);
        }

        $existing = glob($dir . '/[0-9]*.sql') ?: [];
        $lastNum = 0;
        foreach ($existing as $file) {
            if (preg_match('/^(\d{4})_/', basename($file), $m) === 1) {
                $num = (int)$m[1];
                if ($num > $lastNum) {
                    $lastNum = $num;
                }
            }
        }
        $nextNum = str_pad((string)($lastNum + 1), 4, '0', STR_PAD_LEFT);

        $slug = strtolower((string)preg_replace('/[^a-zA-Z0-9]+/', '_', $name));
        $slug = trim($slug, '_');
        if ($slug === '') {
            fwrite(STDERR, "ERROR: nombre inválido tras normalizar.\n");
            exit(1);
        }

        $filename = "{$nextNum}_{$slug}.sql";
        $filepath = $dir . '/' . $filename;

        $template = "-- Innovium · migration {$nextNum}\n"
                  . "-- {$name}\n"
                  . "-- Tipo: {$type}\n"
                  . "-- Generada: " . date('Y-m-d H:i:s') . "\n\n"
                  . "-- TODO: escribir el SQL acá.\n"
                  . "-- Recordá:\n"
                  . "--   · charset utf8mb4_unicode_ci\n"
                  . "--   · engine InnoDB\n"
                  . "--   · timestamps creado_en y actualizado_en DATETIME\n"
                  . "--   · soft delete eliminado_en DATETIME NULL si aplica\n";

        if (file_put_contents($filepath, $template) === false) {
            fwrite(STDERR, "ERROR: no puedo escribir {$filepath}\n");
            exit(1);
        }

        echo "Migration creada: database/migrations/{$type}/{$filename}\n";
        break;

    case 'migrate:tenant':
        if (!isset($argv[2])) {
            fwrite(STDERR, "Uso: php scripts/innovium migrate:tenant <slug>\n");
            exit(1);
        }

        $slug = (string)$argv[2];
        $dir  = INNOVIUM_ROOT . '/database/migrations/tenant';

        try {
            $pdo = App\Core\Database::tenant($slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: si la BD no existe todavía, corré primero 'db:create-demo' (slug=demo) o creala manualmente.\n");
            exit(1);
        }

        try {
            $migrator = new App\Core\Migrator($pdo, $dir);
            $newlyApplied = $migrator->migrate();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR: " . $e->getMessage() . "\n");
            exit(1);
        }

        if (count($newlyApplied) === 0) {
            echo "Schema al día en innovium_{$slug}. Nada que aplicar.\n";
        } else {
            echo count($newlyApplied) . " migración(es) aplicada(s) en innovium_{$slug}:\n";
            foreach ($newlyApplied as $filename) {
                echo "  · {$filename}\n";
            }
        }

        // Sembrar catálogos chilenos (Sprint 1.5a). Idempotente vía
        // INSERT IGNORE: si los catálogos ya están, no duplica nada.
        // Para tenants nuevos (post-Sprint 1.5a), esto los siembra
        // automáticamente. Para tenants existentes, usar el comando
        // dedicado catalogos:seed.
        if (function_exists('innovium_seed_catalogos_chilenos')) {
            innovium_seed_catalogos_chilenos($pdo, $slug);
        } else {
            require_once INNOVIUM_ROOT . '/database/seeds/catalogos_seeder.php';
            innovium_seed_catalogos_chilenos($pdo, $slug);
        }
        break;

    case 'db:create-demo':
        $slug      = 'demo';
        $prefix    = (string)App\Core\Config::get('TENANT_DB_PREFIX', 'innovium_');
        $dbName    = $prefix . $slug;
        $dir       = INNOVIUM_ROOT . '/database/migrations/tenant';

        echo "Creando BD '{$dbName}' (si no existe)...\n";

        try {
            $serverPdo = App\Core\Database::server();
            $serverPdo->exec(
                "CREATE DATABASE IF NOT EXISTS `{$dbName}` " .
                "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
            );
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR creando '{$dbName}': " . $e->getMessage() . "\n");
            exit(1);
        }
        echo "BD '{$dbName}' lista.\n";

        echo "Aplicando migraciones a '{$dbName}'...\n";
        try {
            $pdo          = App\Core\Database::tenant($slug);
            $migrator     = new App\Core\Migrator($pdo, $dir);
            $newlyApplied = $migrator->migrate();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR aplicando migraciones: " . $e->getMessage() . "\n");
            exit(1);
        }

        if (count($newlyApplied) === 0) {
            echo "Schema ya estaba al día.\n";
        } else {
            echo count($newlyApplied) . " migración(es) aplicada(s):\n";
            foreach ($newlyApplied as $filename) {
                echo "  · {$filename}\n";
            }
        }

        echo "\n";
        echo "BD demo lista. Para sembrar roles, permisos y usuarios demo:\n";
        echo "  php scripts/innovium db:seed-demo\n";
        break;

    case 'db:seed-demo':
        $slug   = 'demo';
        $prefix = (string)App\Core\Config::get('TENANT_DB_PREFIX', 'innovium_');
        $dbName = $prefix . $slug;

        echo "Sembrando datos demo en '{$dbName}'...\n";

        try {
            $pdo = App\Core\Database::tenant($slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: corré primero 'php scripts/innovium db:create-demo'.\n");
            exit(1);
        }

        require_once INNOVIUM_ROOT . '/database/seeds/tenant/DemoTenantSeeder.php';

        try {
            $seeder = new DemoTenantSeeder($pdo);
            $counts = $seeder->run();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR sembrando: " . $e->getMessage() . "\n");
            exit(1);
        }

        echo "Seed completo. Totales en BD:\n";
        foreach ($counts as $table => $n) {
            echo "  · {$table}: {$n}\n";
        }
        echo "\n";
        echo "Usuarios creados (password de todos: 'Innovium2026!'):\n";
        echo "  · superadmin@demo.cl\n";
        echo "  · admin@demo.cl\n";
        echo "  · gerente@demo.cl\n";
        echo "  · contador@demo.cl\n";
        echo "  · vendedor@demo.cl\n";
        echo "  · operativo@demo.cl\n";
        break;

    case 'catalogos:seed':
        // Sprint 1.5a · siembra catálogos chilenos en un tenant existente
        // (cuyas migrations 0024+ ya estaban aplicadas, así que migrate:tenant
        // no llamó al seeder automático).
        // Idempotente: re-correr no duplica nada.
        if (!isset($argv[2])) {
            fwrite(STDERR, "Uso: php scripts/innovium catalogos:seed <slug>\n");
            fwrite(STDERR, "Siembra los catálogos chilenos del Sprint 1.5a en un tenant existente.\n");
            exit(1);
        }
        $slug = (string) $argv[2];

        try {
            $pdo = App\Core\Database::tenant($slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR conectando al tenant '{$slug}': " . $e->getMessage() . "\n");
            exit(1);
        }

        require_once INNOVIUM_ROOT . '/database/seeds/catalogos_seeder.php';

        echo "Sembrando catálogos chilenos en innovium_{$slug}...\n";
        try {
            $resumen = innovium_seed_catalogos_chilenos($pdo, $slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR sembrando: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: si las tablas no existen, corré primero 'migrate:tenant {$slug}'.\n");
            exit(1);
        }

        echo "\n";
        echo "Aplicado:\n";
        foreach ($resumen as $archivo => $afectadas) {
            $marca = $archivo === '__demo_secuencia_ni_1148'
                ? "* excepción demo: NI inicializado en 1148 (siguiente=1149)"
                : "{$archivo} → {$afectadas} fila(s) nuevas";
            echo "  · {$marca}\n";
        }
        if ($resumen === []) {
            echo "  (nada — los seeds ya estaban aplicados)\n";
        }

        // Counts finales para verificación.
        echo "\n";
        echo "Counts en innovium_{$slug}:\n";
        $tablas = ['entidades_previsionales', 'parentescos', 'estados_civiles', 'nacionalidades',
                   'regiones', 'provincias', 'comunas', 'capillas', 'tipos_carroza', 'sucursales'];
        foreach ($tablas as $t) {
            try {
                $n = (int) $pdo->query("SELECT COUNT(*) FROM `{$t}`")->fetchColumn();
                echo '  · ' . str_pad($t, 28) . " {$n}\n";
            } catch (\Throwable) {
                echo '  · ' . str_pad($t, 28) . " (tabla inexistente)\n";
            }
        }
        try {
            $sec = $pdo->query("SELECT tipo_contrato, ultimo_numero FROM contratos_secuencias ORDER BY tipo_contrato")->fetchAll(\PDO::FETCH_ASSOC);
            foreach ($sec as $s) {
                echo "  · contratos_secuencias[{$s['tipo_contrato']}]   {$s['ultimo_numero']}\n";
            }
        } catch (\Throwable) {
            // tabla no existe todavía
        }
        break;

    case 'master:migrate':
        $name = (string)App\Core\Config::get('MASTER_DB_NAME', 'innovium_master');
        $dir  = INNOVIUM_ROOT . '/database/migrations/master';

        echo "Creando BD '{$name}' (si no existe)...\n";

        try {
            $serverPdo = App\Core\Database::server();
            $serverPdo->exec(
                "CREATE DATABASE IF NOT EXISTS `{$name}` " .
                "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
            );
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR creando '{$name}': " . $e->getMessage() . "\n");
            exit(1);
        }
        echo "BD '{$name}' lista.\n";

        echo "Aplicando migraciones a '{$name}'...\n";
        try {
            $pdo          = App\Core\Database::master();
            $migrator     = new App\Core\Migrator($pdo, $dir);
            $newlyApplied = $migrator->migrate();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR aplicando migraciones: " . $e->getMessage() . "\n");
            exit(1);
        }

        if (count($newlyApplied) === 0) {
            echo "Schema ya estaba al día. Nada que aplicar.\n";
        } else {
            echo count($newlyApplied) . " migración(es) aplicada(s):\n";
            foreach ($newlyApplied as $filename) {
                echo "  · {$filename}\n";
            }
        }
        break;

    case 'db:seed-master':
        // Siembra master.tenants + tenant_features + tenant_billing para
        // demo e infinia. Idempotente vía ON DUPLICATE KEY UPDATE / INSERT IGNORE.
        try {
            $masterPdo = App\Core\Database::master();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR conectando a master: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: corré 'php scripts/innovium master:migrate' primero.\n");
            exit(1);
        }

        require_once INNOVIUM_ROOT . '/database/seeds/master/MasterSeeder.php';

        try {
            $seeder = new MasterSeeder($masterPdo);
            $counts = $seeder->run();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR sembrando master: " . $e->getMessage() . "\n");
            exit(1);
        }

        echo "Seed completo en innovium_master:\n";
        foreach ($counts as $table => $n) {
            echo "  · {$table}: {$n}\n";
        }
        echo "\n";
        echo "Tenants registrados:\n";
        echo "  · demo    → innovium_demo    (estado=activo)\n";
        echo "  · infinia → innovium_infinia (estado=activo)\n";
        break;

    case 'db:create-infinia':
        // Crea la BD innovium_infinia y aplica las 8 migrations tenant.
        // No siembra usuarios — eso es db:seed-infinia.
        $slug   = 'infinia';
        $prefix = (string)App\Core\Config::get('TENANT_DB_PREFIX', 'innovium_');
        $dbName = $prefix . $slug;
        $dir    = INNOVIUM_ROOT . '/database/migrations/tenant';

        echo "Creando BD '{$dbName}' (si no existe)...\n";

        try {
            $serverPdo = App\Core\Database::server();
            $serverPdo->exec(
                "CREATE DATABASE IF NOT EXISTS `{$dbName}` " .
                "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
            );
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR creando '{$dbName}': " . $e->getMessage() . "\n");
            exit(1);
        }
        echo "BD '{$dbName}' lista.\n";

        echo "Aplicando migraciones a '{$dbName}'...\n";
        try {
            $pdo          = App\Core\Database::tenant($slug);
            $migrator     = new App\Core\Migrator($pdo, $dir);
            $newlyApplied = $migrator->migrate();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR aplicando migraciones: " . $e->getMessage() . "\n");
            exit(1);
        }

        if (count($newlyApplied) === 0) {
            echo "Schema ya estaba al día.\n";
        } else {
            echo count($newlyApplied) . " migración(es) aplicada(s):\n";
            foreach ($newlyApplied as $filename) {
                echo "  · {$filename}\n";
            }
        }

        echo "\n";
        echo "BD infinia lista. Para sembrar roles, permisos y usuarios:\n";
        echo "  php scripts/innovium db:seed-infinia\n";
        break;

    case 'db:seed-infinia':
        $slug   = 'infinia';
        $prefix = (string)App\Core\Config::get('TENANT_DB_PREFIX', 'innovium_');
        $dbName = $prefix . $slug;

        echo "Sembrando datos en '{$dbName}'...\n";

        try {
            $pdo = App\Core\Database::tenant($slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: corré primero 'php scripts/innovium db:create-infinia'.\n");
            exit(1);
        }

        require_once INNOVIUM_ROOT . '/database/seeds/tenant/InfiniaTenantSeeder.php';

        try {
            $seeder = new InfiniaTenantSeeder($pdo);
            $counts = $seeder->run();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR sembrando: " . $e->getMessage() . "\n");
            exit(1);
        }

        echo "Seed completo. Totales en BD:\n";
        foreach ($counts as $table => $n) {
            echo "  · {$table}: {$n}\n";
        }
        echo "\n";
        echo "Usuarios creados (password de todos: 'Innovium2026!'):\n";
        echo "  · superadmin@infinia.cl\n";
        echo "  · admin@infinia.cl\n";
        echo "  · gerente@infinia.cl\n";
        echo "  · contador@infinia.cl\n";
        echo "  · vendedor@infinia.cl\n";
        echo "  · operativo@infinia.cl\n";
        break;

    case 'db:seed-catalogo':
        // Sprint 1.3: siembra tenant_config + precios_uf inicial en
        // un tenant. Idempotente: re-correr no duplica nada.
        if (!isset($argv[2])) {
            fwrite(STDERR, "Uso: php scripts/innovium db:seed-catalogo <slug>\n");
            exit(1);
        }
        $slug = (string)$argv[2];

        echo "Sembrando catálogo baseline en innovium_{$slug}...\n";

        try {
            $pdo = App\Core\Database::tenant($slug);
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR conectando al tenant '{$slug}': " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: corré 'migrate:tenant {$slug}' primero.\n");
            exit(1);
        }

        require_once INNOVIUM_ROOT . '/database/seeds/tenant/CatalogoBaselineSeeder.php';

        try {
            $seeder = new CatalogoBaselineSeeder($pdo);
            $stats  = $seeder->run();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR sembrando catálogo: " . $e->getMessage() . "\n");
            exit(1);
        }

        echo "  · tenant_config insertados:  {$stats['settings_insertados']}\n";
        echo "  · tenant_config existentes:  {$stats['settings_existentes']}\n";
        echo "  · precios_uf hoy:            " . ($stats['precio_uf_insertado'] ? 'insertado' : 'ya existía') . "\n";
        echo "\n";
        echo "Settings sembrados:\n";
        echo "  · valor_uf_actual = 38500 (decimal, monetario)\n";
        echo "  · iva_porcentaje  = 19    (decimal, monetario)\n";
        echo "  · moneda_default  = CLP   (string,  monetario)\n";
        break;

    case 'tenant:create':
        // Crea un tenant nuevo end-to-end:
        //   1. Valida slug (regex + reservados + no-existe en master + BD no-existe)
        //   2. CREATE DATABASE innovium_<slug>
        //   3. Aplica migrations tenant
        //   4. Siembra RBAC + 1 admin con password aleatoria
        //   5. INSERT en master.tenants + tenant_features + tenant_audit_log
        //   6. Rollback total si algo falla a mitad

        if (!isset($argv[2])) {
            fwrite(STDERR, "Uso: php scripts/innovium tenant:create <slug> --name=\"Nombre Legal\" --short=\"Nombre Corto\" [--features=feat1,feat2]\n");
            exit(1);
        }

        // NO aplicar strtolower acá: el comando tenant:create es estricto y
        // rechaza uppercase para evitar dos slugs visualmente distintos que
        // colisionen al normalizarse. El TenantResolver en runtime sí es
        // case-insensitive (Request::fromGlobals lowercase el host).
        $slug = (string)$argv[2];
        $name = null;
        $short = null;
        $extraFeatures = [];

        // Parse flags --key=value (acepta --key="value with spaces")
        for ($i = 3; $i < count($argv); $i++) {
            $arg = (string)$argv[$i];
            if (preg_match('/^--([a-z]+)=(.*)$/', $arg, $m) === 1) {
                $key = $m[1];
                $val = $m[2];
                if ($key === 'name')          { $name = $val; }
                elseif ($key === 'short')     { $short = $val; }
                elseif ($key === 'features')  {
                    $extraFeatures = array_filter(array_map('trim', explode(',', $val)), fn($s) => $s !== '');
                }
                else {
                    fwrite(STDERR, "ERROR: flag desconocido '--{$key}'.\n");
                    exit(1);
                }
            } else {
                fwrite(STDERR, "ERROR: argumento inválido '{$arg}'. Usá --key=value.\n");
                exit(1);
            }
        }

        if ($name === null || $short === null) {
            fwrite(STDERR, "ERROR: faltan flags --name=\"...\" y/o --short=\"...\".\n");
            exit(1);
        }

        // Validar slug — regex + reservados (misma fuente de verdad que TenantResolver).
        if (preg_match(App\Core\TenantResolver::SLUG_REGEX, $slug) !== 1) {
            fwrite(STDERR, "ERROR: slug '{$slug}' inválido. Debe matchear " . App\Core\TenantResolver::SLUG_REGEX . " (empieza con letra minúscula, 3-31 chars, solo a-z/0-9/-).\n");
            exit(1);
        }
        if (in_array($slug, App\Core\TenantResolver::RESERVED_SLUGS, true)) {
            fwrite(STDERR, "ERROR: slug '{$slug}' está reservado. Reservados: " . implode(', ', App\Core\TenantResolver::RESERVED_SLUGS) . ".\n");
            exit(1);
        }

        $prefix = (string)App\Core\Config::get('TENANT_DB_PREFIX', 'innovium_');
        $dbName = $prefix . $slug;
        $storagePath = "tenants/{$slug}";
        $tenantDir = INNOVIUM_ROOT . '/database/migrations/tenant';

        // Pre-flight: master accesible y tenant no existe.
        try {
            $masterPdo = App\Core\Database::master();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR conectando a master: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Sugerencia: corré 'php scripts/innovium master:migrate' primero.\n");
            exit(1);
        }

        $stmt = $masterPdo->prepare('SELECT id FROM tenants WHERE slug = ? LIMIT 1');
        $stmt->execute([$slug]);
        if ($stmt->fetch()) {
            fwrite(STDERR, "ERROR: ya existe un tenant con slug '{$slug}' en master.tenants.\n");
            exit(1);
        }

        // Pre-flight: BD no existe (si existiera, abortamos sin crearla — podría tener datos).
        try {
            $serverPdo = App\Core\Database::server();
        } catch (\Throwable $e) {
            fwrite(STDERR, "ERROR conectando al servidor MySQL: " . $e->getMessage() . "\n");
            exit(1);
        }
        $stmt = $serverPdo->prepare('SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ? LIMIT 1');
        $stmt->execute([$dbName]);
        if ($stmt->fetch()) {
            fwrite(STDERR, "ERROR: la BD '{$dbName}' ya existe en MySQL pero no hay tenant en master. Limpiá manualmente antes de seguir.\n");
            exit(1);
        }

        // Generar password aleatoria para el admin (16 chars, alfabeto sin ambigüedades 0/O/1/l/I).
        $alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
        $alphaMax = strlen($alphabet) - 1;
        $adminPassword = '';
        for ($i = 0; $i < 16; $i++) {
            $adminPassword .= $alphabet[random_int(0, $alphaMax)];
        }

        $adminEmail = "admin@{$slug}.cl";
        $adminNombre = "Administrador {$short}";
        // RUT placeholder válido por formato (el admin debe cambiarlo después).
        $adminRut = '11.111.111-1';

        // Default features + extras del flag.
        $defaultFeatures = ['multi_tenant', 'firma_digital', 'offline_mode'];
        $allFeatures = array_values(array_unique(array_merge($defaultFeatures, $extraFeatures)));

        // Estado para rollback granular.
        $createdDatabase = false;
        $createdTenantId = null;

        try {
            echo "Creando BD '{$dbName}'...\n";
            $serverPdo->exec(
                "CREATE DATABASE `{$dbName}` " .
                "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
            );
            $createdDatabase = true;

            echo "Aplicando migraciones tenant...\n";
            $tenantPdo = App\Core\Database::tenant($slug);
            $migrator = new App\Core\Migrator($tenantPdo, $tenantDir);
            $applied = $migrator->migrate();
            echo "  · " . count($applied) . " migración(es) aplicada(s).\n";

            echo "Sembrando RBAC y admin inicial...\n";
            require_once INNOVIUM_ROOT . '/database/seeds/tenant/NewTenantSeeder.php';
            $seeder = new NewTenantSeeder($tenantPdo, $adminEmail, $adminNombre, $adminRut, $adminPassword);
            $counts = $seeder->run();
            echo "  · roles={$counts['roles']}, permissions={$counts['permissions']}, users={$counts['users']}\n";

            echo "Registrando tenant en master.tenants...\n";
            $stmt = $masterPdo->prepare(
                'INSERT INTO tenants (slug, nombre, nombre_corto, db_name, storage_path, estado) ' .
                'VALUES (?, ?, ?, ?, ?, ?)'
            );
            $stmt->execute([$slug, $name, $short, $dbName, $storagePath, 'activo']);
            $createdTenantId = (int)$masterPdo->lastInsertId();

            echo "Registrando features...\n";
            $stmt = $masterPdo->prepare(
                'INSERT INTO tenant_features (tenant_id, feature_slug, habilitada) VALUES (?, ?, 1)'
            );
            foreach ($allFeatures as $featSlug) {
                $stmt->execute([$createdTenantId, $featSlug]);
            }

            // tenant_billing: una fila vacía con defaults (estado_billing='al_dia').
            $masterPdo
                ->prepare('INSERT INTO tenant_billing (tenant_id) VALUES (?)')
                ->execute([$createdTenantId]);

            // Audit log cross-tenant.
            $detalle = json_encode([
                'slug'         => $slug,
                'nombre'       => $name,
                'nombre_corto' => $short,
                'db_name'      => $dbName,
                'storage_path' => $storagePath,
                'features'     => $allFeatures,
                'admin_email'  => $adminEmail,
            ], JSON_UNESCAPED_UNICODE);
            $masterPdo
                ->prepare('INSERT INTO tenant_audit_log (tenant_id, accion, detalle, ip) VALUES (?, ?, ?, ?)')
                ->execute([$createdTenantId, 'tenant.creado', $detalle !== false ? $detalle : null, 'cli']);

        } catch (\Throwable $e) {
            // Rollback en orden inverso. Cada paso try/catch para no enmascarar
            // el error original si el rollback también falla.
            fwrite(STDERR, "\nERROR durante creación: " . $e->getMessage() . "\n");
            fwrite(STDERR, "Iniciando rollback...\n");

            if ($createdTenantId !== null) {
                try {
                    $masterPdo->prepare('DELETE FROM tenant_audit_log WHERE tenant_id = ?')->execute([$createdTenantId]);
                    $masterPdo->prepare('DELETE FROM tenant_billing WHERE tenant_id = ?')->execute([$createdTenantId]);
                    $masterPdo->prepare('DELETE FROM tenant_features WHERE tenant_id = ?')->execute([$createdTenantId]);
                    $masterPdo->prepare('DELETE FROM tenants WHERE id = ?')->execute([$createdTenantId]);
                    fwrite(STDERR, "  · filas en master eliminadas\n");
                } catch (\Throwable $rb) {
                    fwrite(STDERR, "  · ⚠ falló cleanup de master: " . $rb->getMessage() . "\n");
                    fwrite(STDERR, "    Limpiar manualmente: DELETE FROM tenants WHERE id = {$createdTenantId};\n");
                }
            }
            if ($createdDatabase) {
                try {
                    $serverPdo->exec("DROP DATABASE `{$dbName}`");
                    fwrite(STDERR, "  · BD '{$dbName}' eliminada\n");
                } catch (\Throwable $rb) {
                    fwrite(STDERR, "  · ⚠ falló DROP DATABASE: " . $rb->getMessage() . "\n");
                    fwrite(STDERR, "    Limpiar manualmente: DROP DATABASE `{$dbName}`;\n");
                }
            }
            fwrite(STDERR, "Rollback completo.\n");
            exit(1);
        }

        // Resumen final.
        $env = (string)App\Core\Config::get('APP_ENV', 'local');
        $hostBase = $env === 'local' ? 'innovium.test' : (string)App\Core\Config::get('APP_DOMAIN', 'innovium.cl');
        $loginUrl = $env === 'local'
            ? "http://{$slug}.{$hostBase}:8000/login"
            : "https://{$slug}.{$hostBase}/login";

        echo "\n";
        echo "✓ Tenant '{$slug}' creado.\n";
        echo "\n";
        echo "  ID:           {$createdTenantId}\n";
        echo "  Nombre:       {$name}\n";
        echo "  Nombre corto: {$short}\n";
        echo "  BD:           {$dbName}\n";
        echo "  Storage:      {$storagePath}\n";
        echo "  Features:     " . implode(', ', $allFeatures) . "\n";
        echo "\n";
        echo "Admin inicial:\n";
        echo "  Email:        {$adminEmail}\n";
        echo "  Password:     {$adminPassword}\n";
        echo "  RUT inicial:  {$adminRut}  (cambiar al primer login)\n";
        echo "  URL:          {$loginUrl}\n";
        echo "\n";
        echo "⚠ La password NO se guarda en ningún lado. Copiala AHORA.\n";
        echo "⚠ Para que el subdominio resuelva, agregá al archivo hosts:\n";
        echo "  127.0.0.1   {$slug}.{$hostBase}\n";
        break;

    case 'help':
    case '--help':
    case '-h':
        echo <<<HELP

  Innovium CLI · v\x20

HELP;
        echo INNOVIUM_VERSION . "\n\n";
        echo <<<HELP
  Comandos disponibles:

    serve [host] [port]               Levanta dev server (default localhost:8000)
    key:generate                      Genera APP_KEY y la escribe a .env
    make:migration <name> [type]      Crea migración numerada (type: master|tenant, default tenant)
    migrate:tenant <slug>             Aplica migraciones tenant pendientes a innovium_<slug>
    db:create-demo                    Crea BD innovium_demo y aplica migraciones tenant
    db:seed-demo                      Siembra roles, permisos y usuarios demo en innovium_demo
    master:migrate                    Crea BD innovium_master (si no existe) y aplica migraciones master
    db:seed-master                    Siembra master.tenants/features/billing para demo + infinia
    db:create-infinia                 Crea BD innovium_infinia y aplica migraciones tenant
    db:seed-infinia                   Siembra roles, permisos y 6 usuarios sintéticos en innovium_infinia
    db:seed-catalogo <slug>           Siembra tenant_config + precios_uf inicial en un tenant (Sprint 1.3)
    catalogos:seed <slug>             Siembra catálogos chilenos NI (entidades, comunas, etc.) en un tenant existente (Sprint 1.5a)
    tenant:create <slug> --name --short   Crea tenant nuevo end-to-end (BD + migrations + RBAC + admin + master)
    help                              Muestra esta ayuda

  Ejemplos:

    php scripts/innovium serve
    php scripts/innovium serve 0.0.0.0 8080
    php scripts/innovium key:generate
    php scripts/innovium make:migration crear_tabla_tenants master
    php scripts/innovium master:migrate
    php scripts/innovium db:create-demo
    php scripts/innovium migrate:tenant demo
    php scripts/innovium db:seed-demo
    php scripts/innovium tenant:create test123 --name="Funeraria Test" --short="Test"
    php scripts/innovium tenant:create acoger --name="Funeraria Acoger SpA" --short="Acoger" --features=acompanamiento_duelo,obituarios_publicos


HELP;
        break;

    default:
        fwrite(STDERR, "ERROR: comando desconocido '{$command}'. Probá: php scripts/innovium help\n");
        exit(1);
}

exit($exitCode);
