<?php
/**
 * ENVIEX · consola de instalación y mantenimiento
 *
 *   php bin/enviex migrate                 aplica las migraciones pendientes
 *   php bin/enviex seed                    carga permisos, roles, empresa, sucursal, series, zonas, rutas, servicios y tarifas
 *   php bin/enviex cargar-cp [archivo]     carga códigos postales (por defecto el catálogo incluido; acepta el .txt de SEPOMEX)
 *   php bin/enviex etiquetar-cp [archivo] [--modalidad=diaria|especial] [--reemplazar]
 *                                          etiqueta colonias como entrega diaria o solo cotización especial
 *                                          (por defecto database/seeds/cp-modalidad.csv: listas EAD y especiales de ENVIEX)
 *   php bin/enviex crear-admin             crea un administrador (pide los datos)
 *   php bin/enviex demo                    crea usuarios de prueba por rol (solo ambientes de prueba)
 *   php bin/enviex instalar                migrate + seed + crear-admin
 *   php bin/enviex csd-prueba              carga el CSD de pruebas del SAT como emisor (solo APP_DEBUG=true)
 */
if (PHP_SAPI !== 'cli') {
    exit('Solo por consola.');
}
require dirname(__DIR__) . '/bootstrap.php';

use Enviex\App;
use Enviex\Db\Migrator;
use Enviex\Services\CoberturaImportador;
use Enviex\Services\Seeder;

$comando = isset($argv[1]) ? $argv[1] : 'ayuda';

function preguntar(string $texto, bool $oculto = false): string
{
    echo $texto . ': ';
    if ($oculto && DIRECTORY_SEPARATOR === '/') {
        system('stty -echo');
        $v = trim((string) fgets(STDIN));
        system('stty echo');
        echo PHP_EOL;
        return $v;
    }
    return trim((string) fgets(STDIN));
}

function migrar(): void
{
    $m = new Migrator(App::db(), App::ruta('database/migrations'));
    $hechas = $m->migrar();
    echo $hechas ? 'Migraciones aplicadas: ' . implode(', ', $hechas) . PHP_EOL : 'La base ya está al día.' . PHP_EOL;
}

function sembrar(): void
{
    $r = (new Seeder(App::db()))->aplicar(require App::ruta('database/seeds/catalogo.php'));
    echo 'Catálogo aplicado: ' . json_encode($r) . PHP_EOL;
    $r = (new CoberturaImportador(App::db()))->estructura(require App::ruta('database/seeds/cobertura.php'));
    echo 'Cobertura (zonas, rutas, localidades): ' . json_encode($r) . PHP_EOL;
    $r = (new Seeder(App::db()))->aplicarComercial(require App::ruta('database/seeds/comercial.php'));
    echo 'Servicios y tarifas: ' . json_encode($r) . PHP_EOL;
}

function etiquetarCp(array $argv): void
{
    $archivo = App::ruta('database/seeds/cp-modalidad.csv');
    $modalidad = null;
    $reemplazar = false;
    foreach (array_slice($argv, 2) as $a) {
        if (strpos($a, '--modalidad=') === 0) {
            $modalidad = substr($a, 12);
        } elseif ($a === '--reemplazar') {
            $reemplazar = true;
        } elseif ($a !== '' && $a[0] !== '-') {
            $archivo = $a;
        }
    }
    if ($modalidad !== null && !isset(\Enviex\Services\ModalidadCp::MODALIDADES[$modalidad])) {
        throw new RuntimeException('--modalidad debe ser diaria o especial.');
    }
    $r = (new \Enviex\Services\ModalidadCp(App::db()))->aplicarCsv($archivo, $modalidad, $reemplazar);
    echo 'Etiquetas aplicadas: ' . $r['filas'] . ' filas leídas · ' . $r['etiquetadas'] . ' por colonia · ' . $r['por_cp'] . ' por C.P. · '
        . $r['sin_cp'] . ' C.P. que no están en el catálogo. Colonias con entrega diaria: ' . $r['diaria'] . '; solo cotización especial: ' . $r['especial'] . '.' . PHP_EOL;
    if ($r['faltantes']) {
        echo 'Sin empatar (primeros): ' . implode('; ', $r['faltantes']) . PHP_EOL;
    }
}

function cargarCp(array $argv): void
{
    $archivo = isset($argv[2]) ? $argv[2] : App::ruta('database/seeds/datos/codigos_postales.csv');
    if (!is_file($archivo)) {
        throw new RuntimeException('No encuentro el archivo ' . $archivo);
    }
    $alias = [];
    foreach ((require App::ruta('database/seeds/cobertura.php'))['rutas'] as $r) {
        $alias[$r[0]] = $r[5];
    }
    $opc = ['estado' => '', 'por_municipio' => false];
    foreach (array_slice($argv, 3) as $a) {
        if (strpos($a, '--estado=') === 0) {
            $opc['estado'] = substr($a, 9);
        } elseif ($a === '--por-municipio') {
            $opc['por_municipio'] = true;
        }
    }
    $t = microtime(true);
    $r = (new CoberturaImportador(App::db()))->importar($archivo, $alias, $opc);
    echo 'Códigos postales: ' . json_encode(array_diff_key($r, ['errores' => 1]), JSON_UNESCAPED_UNICODE)
        . sprintf(' en %.1f s', microtime(true) - $t) . PHP_EOL;
    foreach ($r['errores'] as $e) {
        echo '  ! ' . $e . PHP_EOL;
    }
}

function crearAdmin(array $argv): void
{
    $nombre  = isset($argv[2]) ? $argv[2] : preguntar('Nombre');
    $usuario = isset($argv[3]) ? $argv[3] : preguntar('Usuario');
    $correo  = isset($argv[4]) ? $argv[4] : preguntar('Correo');
    $clave   = isset($argv[5]) ? $argv[5] : preguntar('Contraseña (8+ caracteres, letras y números)', true);
    $id = (new Seeder(App::db()))->crearUsuario('administrador', $nombre, $usuario, $correo, $clave, false);
    echo 'Administrador creado con id ' . $id . PHP_EOL;
}

try {
    switch ($comando) {
        case 'migrate':
            migrar();
            break;
        case 'seed':
            sembrar();
            break;
        case 'crear-admin':
            crearAdmin($argv);
            break;
        case 'cargar-cp':
            cargarCp($argv);
            break;
        case 'etiquetar-cp':
            etiquetarCp($argv);
            break;
        case 'instalar':
            migrar();
            sembrar();
            cargarCp([]);
            etiquetarCp([]);
            crearAdmin($argv);
            break;
        case 'demo':
            if (App::debug() === false) {
                throw new RuntimeException('demo solo corre con APP_DEBUG=true (ambiente de prueba).');
            }
            $s = new Seeder(App::db());
            $usuarios = [
                ['supervisor', 'Sofía Supervisora', 'supervisor', 'supervisor@demo.enviex.mx'],
                ['capturista', 'Carlos Capturista', 'capturista', 'capturista@demo.enviex.mx'],
                ['facturacion', 'Fernanda Facturación', 'facturacion', 'facturacion@demo.enviex.mx'],
                ['operador', 'Óscar Operador', 'operador', 'operador@demo.enviex.mx'],
            ];
            foreach ($usuarios as $u) {
                try {
                    $s->crearUsuario($u[0], $u[1], $u[2], $u[3], 'Enviex2026', false);
                    echo 'Usuario demo: ' . $u[2] . ' / Enviex2026' . PHP_EOL;
                } catch (InvalidArgumentException $e) {
                    echo $u[2] . ': ' . $e->getMessage() . PHP_EOL;
                }
            }
            break;
        case 'csd-prueba':
            // Carga el CSD de pruebas público del SAT (EKU9003173C9) y pone sus datos como emisor. Nunca en producción.
            if (App::debug() === false) {
                throw new RuntimeException('csd-prueba solo corre con APP_DEBUG=true (ambiente de prueba).');
            }
            $dir = App::ruta('tests/fixtures/csd-prueba/');
            $db = App::db();
            $emp = $db->one('SELECT id FROM empresa ORDER BY id LIMIT 1');
            $db->update('empresa', ['rfc' => 'EKU9003173C9', 'razon_social' => 'ESCUELA KEMPER URGATE', 'regimen_fiscal' => '601'], 'id = :_id', ['_id' => (int) $emp['id']]);
            $r = (new \Enviex\Services\EmisorService($db))->subir(file_get_contents($dir . 'EKU9003173C9.cer'), file_get_contents($dir . 'EKU9003173C9.key'), '12345678a');
            echo 'CSD de prueba activo: ' . $r['no_certificado'] . ' (vence ' . $r['vigente_hasta'] . '). Emisor: EKU9003173C9 ESCUELA KEMPER URGATE, régimen 601.' . PHP_EOL;
            break;
        default:
            echo "Uso: php bin/enviex migrate | seed | cargar-cp [archivo] | etiquetar-cp [archivo] | crear-admin | instalar | demo | csd-prueba\n";
    }
} catch (Throwable $e) {
    fwrite(STDERR, 'Error: ' . $e->getMessage() . PHP_EOL);
    exit(1);
}
