<?php
/**
 * Parche de Distribucion NICC v2 - copiar.php
 * ==============================================
 * 1) Extrae sistema.zip a la carpeta sistema/ (una sola vez)
 * 2) Copia los archivos extraidos a cada subdominio DokuWiki
 *
 * Flujo:
 * 1. Autenticacion con token de seguridad
 * 2. Seleccion de letras (A-Z + #) para filtrar subdominios
 * 3. Escaneo AJAX de subdominios por letra
 * 4. Copia AJAX subdominio por subdominio
 * 5. Progreso en tiempo real con opcion de cancelar
 *
 * Opcion de aplicar o ignorar exclusion.txt por subdominio (toggle).
 */

require_once __DIR__ . '/config.php';

set_time_limit(MAX_EXECUTION_TIME);
ini_set('memory_limit', MEMORY_LIMIT);

// Iniciar sesion para compartir autenticacion con estado.php
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

// ============================================================
// FUNCIONES DE ESCANEO (patron probado NICC)
// ============================================================

/**
 * Detecta si una carpeta es una instalacion DokuWiki
 */
function isDokuWiki($path) {
    return file_exists($path . '/doku.php')
        && (file_exists($path . '/conf/local.php') || file_exists($path . '/conf/dokuwiki.php'))
        && is_dir($path . '/data');
}

/**
 * Escanea SOLO el nivel inmediato de SEARCH_PATH buscando instalaciones DokuWiki.
 * Los subdominios siempre estan directamente bajo public_html.
 * NOTA: Salta la carpeta sistema/ (SOURCE_DIR) para no copiarse a si misma
 */
function scanDirectory($path) {
    $sites = [];
    if (!is_dir($path)) return $sites;

    $items = @scandir($path);
    if ($items === false) return $sites;

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        if ($item[0] === '_') continue;               // Ignorar carpetas internas (_siedeployer_patch, etc.)
        $fullPath = $path . '/' . $item;
        if (!is_dir($fullPath)) continue;
        if (is_link($fullPath)) continue;              // Ignorar symlinks (alias cPanel)

        // Saltar la carpeta sistema/ (fuente de copiado) para no copiarse a si misma
        if (realpath($fullPath) === realpath(SOURCE_DIR)) continue;

        if (isDokuWiki($fullPath)) {
            $sites[] = $fullPath;
        }
    }
    return $sites;
}

/**
 * Verifica si un subdominio esta en la lista de exclusion de config.php
 * Retorna el nombre que matcheo o false si no esta excluido
 */
function isExcludedByList($sitePath) {
    global $EXCLUSION_LIST;
    if (empty($EXCLUSION_LIST)) return false;
    $siteName = strtolower(basename($sitePath));
    foreach ($EXCLUSION_LIST as $excluded) {
        if ($siteName === strtolower($excluded)) {
            return $excluded;
        }
    }
    return false;
}

/**
 * Obtiene la letra inicial de un nombre de subdominio
 * Retorna A-Z o # para numeros/especiales
 */
function getLetraFromNombre($nombre) {
    $first = strtoupper(substr($nombre, 0, 1));
    if (ctype_alpha($first)) return $first;
    return '#';
}

// ============================================================
// FUNCIONES DE UTILIDAD
// ============================================================

function formatBytes($bytes) {
    if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB';
    if ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB';
    if ($bytes >= 1024) return number_format($bytes / 1024, 2) . ' KB';
    return $bytes . ' bytes';
}

function writeLog($message) {
    if (!is_dir(LOG_DIR)) {
        @mkdir(LOG_DIR, 0755, true);
    }
    $logFile = LOG_DIR . '/distribucion_' . date('Y-m-d') . '.log';
    file_put_contents($logFile, date('[Y-m-d H:i:s] ') . $message . "\n", FILE_APPEND);
}

function loadProgress() {
    if (file_exists(PROGRESS_FILE)) {
        $data = json_decode(file_get_contents(PROGRESS_FILE), true);
        if (is_array($data)) return $data;
    }
    return [
        'copiados'        => [],
        'excluidos'       => [],
        'errores'         => [],
        'archivos_fuente' => 0,
        'archivos_total_copiados' => 0,
        'bytes_total_copiados'    => 0,
        'usar_exclusiones'        => true,
        'inicio'          => date('Y-m-d H:i:s'),
    ];
}

function saveProgress($progress) {
    file_put_contents(PROGRESS_FILE, json_encode($progress, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}

// ============================================================
// FUNCIONES DE EXTRACCION Y COPIA
// ============================================================

/**
 * Extrae sistema.zip a la carpeta sistema/
 * Retorna: [true, mensaje] o [false, error]
 */
function extractSourceZip() {
    if (!file_exists(SOURCE_ZIP)) {
        return [false, "sistema.zip no encontrado"];
    }

    $zip = new ZipArchive();
    $res = $zip->open(SOURCE_ZIP);

    if ($res !== true) {
        return [false, "No se pudo abrir sistema.zip (error: $res)"];
    }

    // Crear carpeta destino
    if (!is_dir(SOURCE_DIR)) {
        if (!@mkdir(SOURCE_DIR, 0755, true)) {
            $zip->close();
            return [false, "No se pudo crear la carpeta sistema/"];
        }
    }

    // Extraer
    if (!$zip->extractTo(SOURCE_DIR)) {
        $zip->close();
        return [false, "Error al extraer sistema.zip"];
    }

    $numFiles = $zip->numFiles;
    $zip->close();

    return [true, "Extraidos $numFiles archivos a sistema/"];
}

/**
 * Cuenta archivos y tamano total en una carpeta recursivamente
 */
function countSourceFiles($dir) {
    $count = 0;
    $size = 0;
    $items = @scandir($dir);
    if ($items === false) return [0, 0];

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $path = $dir . '/' . $item;
        if (is_dir($path)) {
            list($c, $s) = countSourceFiles($path);
            $count += $c;
            $size += $s;
        } else {
            $count++;
            $size += @filesize($path) ?: 0;
        }
    }
    return [$count, $size];
}

/**
 * Copia recursivamente desde SOURCE_DIR a un subdominio destino.
 * Crea carpetas si no existen, sobrescribe archivos existentes.
 * Retorna: [archivos_copiados, bytes_copiados, errores[]]
 */
function copyToSite($sourceDir, $destDir) {
    $copied = 0;
    $bytes = 0;
    $errors = [];

    $items = @scandir($sourceDir);
    if ($items === false) {
        $errors[] = "No se pudo leer: $sourceDir";
        return [$copied, $bytes, $errors];
    }

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;

        $srcPath = $sourceDir . '/' . $item;
        $dstPath = $destDir . '/' . $item;

        if (is_dir($srcPath)) {
            // Crear carpeta destino si no existe
            if (!is_dir($dstPath)) {
                if (!@mkdir($dstPath, 0755, true)) {
                    $errors[] = "No se pudo crear carpeta: $dstPath";
                    continue;
                }
            }
            // Copiar contenido recursivamente
            list($c, $b, $e) = copyToSite($srcPath, $dstPath);
            $copied += $c;
            $bytes += $b;
            $errors = array_merge($errors, $e);
        } else {
            // Copiar archivo
            $size = @filesize($srcPath) ?: 0;
            if (@copy($srcPath, $dstPath)) {
                $copied++;
                $bytes += $size;
            } else {
                $errors[] = "No se pudo copiar: $item";
            }
        }
    }

    return [$copied, $bytes, $errors];
}

/**
 * Verifica y prepara la fuente de archivos (sistema.zip/sistema/)
 * Retorna array con estado de la fuente
 */
function checkSource() {
    $zipExists = file_exists(SOURCE_ZIP);
    $dirExists = is_dir(SOURCE_DIR);
    $extractMsg = '';

    // Si no existe la carpeta sistema/ pero si el ZIP, extraer
    if (!$dirExists && $zipExists) {
        list($ok, $extractMsg) = extractSourceZip();
        if ($ok) {
            $dirExists = true;
            writeLog("EXTRACCION: $extractMsg");
        } else {
            writeLog("ERROR EXTRACCION: $extractMsg");
        }
    }

    $sourceFileCount = 0;
    $sourceSize = 0;
    $sourceReady = false;

    if ($dirExists) {
        list($sourceFileCount, $sourceSize) = countSourceFiles(SOURCE_DIR);
        $sourceReady = ($sourceFileCount > 0);
    }

    return [
        'ready'      => $sourceReady,
        'fileCount'  => $sourceFileCount,
        'size'       => $sourceSize,
        'sizeHuman'  => formatBytes($sourceSize),
        'zipExists'  => $zipExists,
        'dirExists'  => $dirExists,
        'extractMsg' => $extractMsg,
    ];
}

// ============================================================
// HANDLER AJAX
// ============================================================

if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['accion'])) {
    header('Content-Type: application/json; charset=utf-8');

    // Verificar token en CADA peticion
    $token = isset($_POST['token']) ? $_POST['token'] : '';
    if ($token !== SECURITY_TOKEN) {
        http_response_code(403);
        echo json_encode(['error' => 'Token de seguridad invalido']);
        exit;
    }

    // Marcar sesion como autenticada (para estado.php)
    $_SESSION['distribucion_auth'] = true;

    $accion = $_POST['accion'];

    // ---------------------------------------------------------
    // ESCANEAR: Obtener lista de sistemas por letras seleccionadas
    // ---------------------------------------------------------
    if ($accion === 'escanear') {
        $letrasSeleccionadas = isset($_POST['letras']) ? $_POST['letras'] : [];
        if (!is_array($letrasSeleccionadas)) {
            $letrasSeleccionadas = [$letrasSeleccionadas];
        }

        // Toggle de exclusiones (especifico de distribucion)
        $usarExclusiones = true;
        if (isset($_POST['usarExclusiones'])) {
            $usarExclusiones = ($_POST['usarExclusiones'] === '1');
        }

        // Verificar y preparar fuente
        $source = checkSource();

        $sites = scanDirectory(SEARCH_PATH);
        sort($sites);

        $resultado = [];
        $excluidos = 0;
        $excluidosLista = [];

        foreach ($sites as $sitePath) {
            $nombre = basename($sitePath);
            $letra = getLetraFromNombre($nombre);

            if (!empty($letrasSeleccionadas) && !in_array($letra, $letrasSeleccionadas)) continue;

            // Verificar exclusion
            $excludedBy = isExcludedByList($sitePath);
            $hasExclFile = file_exists($sitePath . '/' . EXCLUSION_FILE);

            if ($usarExclusiones && ($excludedBy || $hasExclFile)) {
                $excluidos++;
                $motivo = $excludedBy ? "lista ($excludedBy)" : 'exclusion.txt';
                $excluidosLista[] = ['nombre' => $nombre, 'motivo' => $motivo];
                continue;
            }

            $resultado[] = [
                'path'   => $sitePath,
                'nombre' => $nombre,
                'letra'  => $letra,
            ];
        }

        echo json_encode([
            'ok'              => true,
            'sistemas'        => $resultado,
            'total'           => count($resultado),
            'excluidos'       => $excluidos,
            'excluidos_lista' => $excluidosLista,
            'source'          => $source,
        ]);
        exit;
    }

    // ---------------------------------------------------------
    // COPIAR: Procesar un subdominio
    // ---------------------------------------------------------
    if ($accion === 'copiar') {
        $sitePath = isset($_POST['sitePath']) ? $_POST['sitePath'] : '';

        // Validar que la ruta esta dentro de SEARCH_PATH
        $realSearch = realpath(SEARCH_PATH);
        $realSite   = realpath($sitePath);
        if (!$realSite || !$realSearch || strpos($realSite, $realSearch) !== 0) {
            echo json_encode(['error' => 'Ruta invalida o fuera del alcance']);
            exit;
        }

        // Validar que es DokuWiki
        if (!isDokuWiki($sitePath)) {
            echo json_encode(['error' => 'No es una instalacion DokuWiki valida']);
            exit;
        }

        // Verificar que la fuente esta lista
        if (!is_dir(SOURCE_DIR)) {
            echo json_encode(['error' => 'Carpeta sistema/ no existe']);
            exit;
        }

        set_time_limit(MAX_EXECUTION_TIME);

        $timeStart = microtime(true);
        list($filesCopied, $bytesCopied, $copyErrors) = copyToSite(SOURCE_DIR, $sitePath);
        $elapsed = round(microtime(true) - $timeStart, 2);

        // Log
        $nombre = basename($sitePath);
        $errCount = count($copyErrors);
        if ($errCount === 0) {
            writeLog("OK: $nombre | $filesCopied archivos (" . formatBytes($bytesCopied) . ") en {$elapsed}s");
        } else {
            writeLog("PARCIAL: $nombre | $filesCopied OK, $errCount errores en {$elapsed}s");
        }

        // Actualizar progreso
        $progress = loadProgress();

        if ($filesCopied > 0) {
            $progress['copiados'][] = $sitePath;
            $progress['archivos_total_copiados'] += $filesCopied;
            $progress['bytes_total_copiados'] += $bytesCopied;
        }

        if ($errCount > 0) {
            $progress['errores'][$nombre] = $errCount . " errores de " . ($filesCopied + $errCount) . " archivos";
        }

        saveProgress($progress);

        echo json_encode([
            'ok'        => true,
            'nombre'    => $nombre,
            'archivos'  => $filesCopied,
            'bytes'     => $bytesCopied,
            'bytesHuman' => formatBytes($bytesCopied),
            'errores'   => $copyErrors,
            'tiempo'    => $elapsed,
        ]);
        exit;
    }

    // ---------------------------------------------------------
    // RESET: Reiniciar progreso
    // ---------------------------------------------------------
    if ($accion === 'reset') {
        if (file_exists(PROGRESS_FILE)) @unlink(PROGRESS_FILE);
        echo json_encode(['ok' => true, 'mensaje' => 'Progreso reiniciado']);
        exit;
    }

    echo json_encode(['error' => 'Accion desconocida: ' . $accion]);
    exit;
}

// ============================================================
// INTERFAZ WEB (HTML + CSS + JavaScript)
// ============================================================
?><!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Distribucion NICC v2</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Segoe UI', Tahoma, sans-serif; background: #1a1a2e; color: #e0e0e0; padding: 20px; }
        .container { max-width: 950px; margin: 0 auto; }
        h1 { color: #74b9ff; margin-bottom: 5px; font-size: 1.4em; }
        h2 { color: #ffd93d; font-size: 1.1em; margin: 15px 0 8px; }
        .subtitle { color: #888; font-size: 0.85em; margin-bottom: 15px; }

        /* Barra de progreso */
        .progress-bar { background: #2a2a4a; border-radius: 8px; height: 30px; margin: 10px 0; overflow: hidden; }
        .progress-fill { height: 100%; border-radius: 8px; transition: width 0.3s; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.85em; }
        .fill-ok { background: linear-gradient(90deg, #00b894, #00cec9); }

        /* Stats */
        .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin: 15px 0; }
        .stat-box { background: #2a2a4a; padding: 12px; border-radius: 8px; text-align: center; }
        .stat-value { font-size: 1.5em; font-weight: bold; }
        .stat-label { font-size: 0.8em; color: #888; }
        .val-red { color: #ff6b6b; }
        .val-green { color: #00b894; }
        .val-yellow { color: #ffd93d; }
        .val-blue { color: #74b9ff; }
        .val-purple { color: #a29bfe; }
        .val-orange { color: #e17055; }

        /* Botones */
        .btn { display: inline-block; padding: 8px 18px; border-radius: 6px; text-decoration: none; font-weight: bold; font-size: 0.9em; margin: 5px 3px; cursor: pointer; border: none; color: white; }
        .btn-primary { background: #0984e3; }
        .btn-warning { background: #f39c12; }
        .btn-danger { background: #d63031; }
        .btn-success { background: #00b894; }
        .btn-info { background: #6c5ce7; }
        .btn-start { background: linear-gradient(90deg, #00b894, #00cec9); color: #000; font-size: 1.1em; padding: 12px 30px; }
        .btn:hover { opacity: 0.9; }
        .btn:disabled { opacity: 0.4; cursor: not-allowed; }

        /* Cajas */
        .site-current { background: #2a2a4a; border-left: 4px solid #74b9ff; padding: 12px; margin: 10px 0; border-radius: 0 8px 8px 0; }
        .site-name { color: #74b9ff; font-weight: bold; }
        .log-box { background: #0d0d1a; border: 1px solid #333; padding: 10px; border-radius: 8px; max-height: 350px; overflow-y: auto; font-family: monospace; font-size: 0.78em; line-height: 1.6; margin: 10px 0; }
        .log-ok { color: #00b894; }
        .log-skip { color: #ffd93d; }
        .log-err { color: #ff6b6b; }
        .log-info { color: #74b9ff; }
        .complete { background: #0d3320; border: 2px solid #00b894; padding: 20px; border-radius: 10px; text-align: center; margin: 20px 0; }
        .complete h2 { color: #00b894; font-size: 1.3em; }
        .cancelled { background: #3d3d00; border: 2px solid #ffd93d; }
        .cancelled h2 { color: #ffd93d; }
        .error-box { background: #3d0d0d; border: 2px solid #ff6b6b; padding: 15px; border-radius: 10px; text-align: center; margin: 20px 0; }
        .warning-box { background: #3d1f00; border: 1px solid #f39c12; padding: 10px; border-radius: 8px; margin: 10px 0; font-size: 0.85em; }

        /* Token gate */
        .token-gate { background: #1a2a4a; border: 2px solid #74b9ff; padding: 30px; border-radius: 12px; margin: 30px 0; text-align: center; }
        .token-gate h2 { color: #74b9ff; margin-bottom: 15px; }
        .token-input { background: #0d0d1a; border: 2px solid #444; color: #fff; padding: 10px 15px; border-radius: 6px; font-size: 1em; width: 300px; text-align: center; }
        .token-input:focus { border-color: #74b9ff; outline: none; }
        .token-error { color: #ff6b6b; margin-top: 10px; display: none; }

        /* Grid de letras */
        .letter-grid { display: flex; flex-wrap: wrap; gap: 6px; margin: 15px 0; justify-content: center; }
        .btn-letter { width: 44px; height: 44px; border-radius: 8px; border: 2px solid #444; background: #2a2a4a; color: #888; font-size: 1em; font-weight: bold; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; }
        .btn-letter:hover { border-color: #74b9ff; color: #74b9ff; }
        .btn-letter-selected { border-color: #00b894; background: #0d3320; color: #00b894; }
        .letter-actions { text-align: center; margin: 10px 0; }

        /* Scan results */
        .scan-result { background: #1a2a4a; border: 1px solid #74b9ff; padding: 15px; border-radius: 8px; margin: 15px 0; }
        .scan-list { max-height: 200px; overflow-y: auto; margin: 10px 0; font-size: 0.85em; }
        .scan-item { padding: 3px 0; color: #ccc; }
        .scan-item .letra-tag { display: inline-block; width: 22px; color: #74b9ff; font-weight: bold; }

        /* Secciones ocultables */
        .section { display: none; }
        .section.active { display: block; }

        /* Procesando animacion */
        @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
        .processing { animation: pulse 1.5s infinite; color: #ffd93d; }

        /* Info banners */
        .info-banner { background: #1a3a1a; border: 1px solid #00b894; padding: 10px; border-radius: 8px; margin: 10px 0; font-size: 0.85em; color: #00b894; text-align: center; }
        .source-info { background: #1a2a4a; border: 1px solid #74b9ff; padding: 12px; border-radius: 8px; margin: 10px 0; }

        /* Toggle exclusiones */
        .option-row { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 15px 0; font-size: 1em; }
        .option-row label { cursor: pointer; user-select: none; }
        .toggle { position: relative; display: inline-block; width: 50px; height: 26px; }
        .toggle input { opacity: 0; width: 0; height: 0; }
        .toggle .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #636e72; border-radius: 26px; transition: .3s; }
        .toggle .slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 3px; bottom: 3px; background-color: white; border-radius: 50%; transition: .3s; }
        .toggle input:checked + .slider { background-color: #00b894; }
        .toggle input:checked + .slider:before { transform: translateX(24px); }
        .excl-hint { color: #888; font-size: 0.82em; margin-top: 5px; text-align: center; }
    </style>
</head>
<body>
<div class="container">
    <h1>Parche de Distribucion NICC v2</h1>
    <p class="subtitle">Extrae sistema.zip una vez, copia archivos a cada subdominio por letras seleccionadas</p>

    <!-- ============================================================ -->
    <!-- SECCION 1: GATE DE TOKEN                                      -->
    <!-- ============================================================ -->
    <div id="secToken" class="section active">
        <div class="token-gate">
            <h2>Autenticacion Requerida</h2>
            <p style="color: #888; margin-bottom: 15px;">Ingrese el token de seguridad para continuar</p>
            <div>
                <input type="password" id="inputToken" class="token-input" placeholder="Token de seguridad"
                       onkeydown="if(event.key==='Enter') verificarToken()">
            </div>
            <div style="margin-top: 15px;">
                <button class="btn btn-primary" onclick="verificarToken()">Verificar</button>
            </div>
            <div id="tokenError" class="token-error">Token invalido</div>
        </div>
    </div>

    <!-- ============================================================ -->
    <!-- SECCION 2: SELECCION DE LETRAS + FUENTE + EXCLUSIONES        -->
    <!-- ============================================================ -->
    <div id="secLetras" class="section">
        <div class="info-banner">Token verificado correctamente</div>

        <!-- Estado de la fuente -->
        <div class="source-info" id="sourceInfo">
            <strong>sistema.zip:</strong> <span id="sourceZipStatus">Verificando...</span>
            <br>
            <strong>sistema/:</strong> <span id="sourceDirStatus">Verificando...</span>
        </div>

        <h2>Seleccionar Letras</h2>
        <p style="color: #888; font-size: 0.85em; margin-bottom: 10px;">
            Seleccione las letras iniciales de los subdominios a distribuir. # = subdominios que comienzan con numero.
        </p>

        <div class="letter-grid" id="letterGrid"></div>

        <div class="letter-actions">
            <button class="btn btn-info" onclick="seleccionarTodos()" style="font-size: 0.8em; padding: 5px 12px;">Todos</button>
            <button class="btn btn-warning" onclick="seleccionarNinguno()" style="font-size: 0.8em; padding: 5px 12px;">Ninguno</button>
        </div>

        <!-- Toggle de exclusiones -->
        <div class="option-row">
            <label class="toggle">
                <input type="checkbox" id="chkExcl" checked>
                <span class="slider"></span>
            </label>
            <label for="chkExcl"><strong>Aplicar exclusiones</strong> (respetar exclusion.txt y lista)</label>
        </div>
        <div class="excl-hint">
            Activado: salta subdominios con <code>exclusion.txt</code> o en la lista de exclusion.<br>
            Desactivado: distribuye a <strong>todos</strong> los subdominios sin excepcion.
        </div>

        <div style="text-align: center; margin: 20px 0;">
            <button id="btnEscanear" class="btn btn-primary" onclick="escanear()" disabled style="font-size: 1em; padding: 10px 25px;">
                ESCANEAR SISTEMAS
            </button>
        </div>

        <!-- Resultado del escaneo -->
        <div id="scanResult" style="display: none;"></div>

        <!-- Boton de iniciar distribucion -->
        <div id="startSection" style="display: none; text-align: center; margin: 20px 0;">
            <div class="warning-box" style="text-align: left; margin-bottom: 15px;">
                <strong style="color: #74b9ff;">ATENCION:</strong> Esta operacion COPIARA archivos de <code>sistema/</code> a los subdominios seleccionados.
                Los archivos existentes con el mismo nombre seran <strong>sobrescritos</strong>.
            </div>
            <button id="btnIniciar" class="btn btn-start" onclick="iniciarDistribucion()">
                INICIAR DISTRIBUCION
            </button>
        </div>

        <div style="margin-top: 15px;">
            <a href="estado.php" class="btn btn-info" style="font-size: 0.8em;">Ver Estado</a>
            <button class="btn btn-warning" onclick="resetProgreso()" style="font-size: 0.8em;">Reiniciar Progreso</button>
        </div>
    </div>

    <!-- ============================================================ -->
    <!-- SECCION 3: PROCESAMIENTO                                      -->
    <!-- ============================================================ -->
    <div id="secProceso" class="section">
        <h2>Distribucion en Progreso</h2>

        <div class="progress-bar">
            <div id="progressFill" class="progress-fill fill-ok" style="width: 0%">0%</div>
        </div>

        <div id="currentSystem" class="site-current" style="display: none;">
            <span class="site-name processing" id="currentName">...</span>
        </div>

        <div class="stats">
            <div class="stat-box">
                <div class="stat-value val-blue" id="statTotal">0</div>
                <div class="stat-label">Total sistemas</div>
            </div>
            <div class="stat-box">
                <div class="stat-value val-green" id="statProcesados">0</div>
                <div class="stat-label">Procesados</div>
            </div>
            <div class="stat-box">
                <div class="stat-value val-purple" id="statArchivos">0</div>
                <div class="stat-label">Archivos copiados</div>
            </div>
            <div class="stat-box">
                <div class="stat-value val-purple" id="statBytes">0 bytes</div>
                <div class="stat-label">Total distribuido</div>
            </div>
            <div class="stat-box">
                <div class="stat-value val-red" id="statErrores">0</div>
                <div class="stat-label">Errores</div>
            </div>
        </div>

        <h2>Log de Operaciones</h2>
        <div id="logBox" class="log-box"></div>

        <div style="margin-top: 15px; text-align: center;">
            <button id="btnCancelar" class="btn btn-danger" onclick="cancelar()">CANCELAR</button>
        </div>
    </div>

    <!-- ============================================================ -->
    <!-- SECCION 4: COMPLETADO                                         -->
    <!-- ============================================================ -->
    <div id="secCompleto" class="section">
        <div id="resumenBox" class="complete">
            <h2 id="resumenTitulo">DISTRIBUCION COMPLETADA</h2>
            <p id="resumenTexto"></p>
        </div>

        <div class="stats" id="resumenStats"></div>

        <h2>Log Final</h2>
        <div id="logFinal" class="log-box"></div>

        <div style="margin-top: 15px; text-align: center;">
            <a href="estado.php" class="btn btn-info">Ver Estado Detallado</a>
            <button class="btn btn-primary" onclick="nuevaDistribucion()">Nueva Distribucion</button>
        </div>
    </div>
</div>

<!-- ============================================================ -->
<!-- JAVASCRIPT                                                     -->
<!-- ============================================================ -->
<script>
// ---- Estado global ----
let token = '';
let sistemas = [];
let currentIndex = 0;
let cancelled = false;
let isRunning = false;  // Bandera anti-doble-ejecucion
let totalStats = { archivos: 0, bytes: 0, errores: 0 };
const letrasSeleccionadas = new Set();

// ---- Escapar HTML para prevenir XSS ----
function escapeHtml(text) {
    const div = document.createElement('div');
    div.appendChild(document.createTextNode(text));
    return div.innerHTML;
}

// ---- Formatear bytes en JS ----
function formatBytesJS(bytes) {
    if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(2) + ' GB';
    if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + ' MB';
    if (bytes >= 1024) return (bytes / 1024).toFixed(2) + ' KB';
    return bytes + ' bytes';
}

// ---- Inicializacion: generar grid de letras ----
(function init() {
    const grid = document.getElementById('letterGrid');
    const letras = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'.split('');
    letras.forEach(function(letra) {
        const btn = document.createElement('button');
        btn.className = 'btn-letter';
        btn.textContent = letra;
        btn.setAttribute('data-letra', letra);
        btn.onclick = function() { toggleLetra(letra, btn); };
        grid.appendChild(btn);
    });
})();

// ---- Funciones de seccion ----
function showSection(id) {
    document.querySelectorAll('.section').forEach(function(s) { s.classList.remove('active'); });
    document.getElementById(id).classList.add('active');
}

// ---- Token ----
function verificarToken() {
    const input = document.getElementById('inputToken');
    token = input.value.trim();
    if (!token) return;

    // Validar token haciendo un escaneo con letras vacias
    const fd = new FormData();
    fd.append('accion', 'escanear');
    fd.append('token', token);

    fetch('copiar.php', { method: 'POST', body: fd })
        .then(function(r) {
            if (r.status === 403) throw new Error('Token invalido');
            return r.json();
        })
        .then(function(data) {
            if (data.error && data.error.indexOf('Token') !== -1) {
                throw new Error('Token invalido');
            }
            // Actualizar info de la fuente
            if (data.source) {
                updateSourceInfo(data.source);
            }
            showSection('secLetras');
        })
        .catch(function() {
            document.getElementById('tokenError').style.display = 'block';
            input.value = '';
            input.focus();
        });
}

// ---- Actualizar info de la fuente ----
function updateSourceInfo(source) {
    const zipEl = document.getElementById('sourceZipStatus');
    const dirEl = document.getElementById('sourceDirStatus');

    if (source.zipExists) {
        zipEl.innerHTML = '<span style="color:#00b894;">Encontrado</span>';
    } else if (source.dirExists) {
        zipEl.innerHTML = '<span style="color:#888;">No necesario (ya extraido)</span>';
    } else {
        zipEl.innerHTML = '<span style="color:#ff6b6b;">NO encontrado</span>';
    }

    if (source.ready) {
        dirEl.innerHTML = '<span style="color:#00b894;">' + escapeHtml(source.fileCount.toLocaleString()) + ' archivos (' + escapeHtml(source.sizeHuman) + ')</span>';
    } else if (source.dirExists) {
        dirEl.innerHTML = '<span style="color:#ff6b6b;">Carpeta vacia</span>';
    } else {
        dirEl.innerHTML = '<span style="color:#ff6b6b;">No existe</span>';
    }

    if (source.extractMsg) {
        dirEl.innerHTML += ' <span style="color:#00b894; font-size:0.85em;">(' + escapeHtml(source.extractMsg) + ')</span>';
    }
}

// ---- Letras ----
function toggleLetra(letra, btn) {
    if (letrasSeleccionadas.has(letra)) {
        letrasSeleccionadas.delete(letra);
        btn.classList.remove('btn-letter-selected');
    } else {
        letrasSeleccionadas.add(letra);
        btn.classList.add('btn-letter-selected');
    }
    document.getElementById('btnEscanear').disabled = (letrasSeleccionadas.size === 0);
    // Ocultar resultado anterior
    document.getElementById('scanResult').style.display = 'none';
    document.getElementById('startSection').style.display = 'none';
}

function seleccionarTodos() {
    document.querySelectorAll('.btn-letter').forEach(function(btn) {
        const letra = btn.getAttribute('data-letra');
        letrasSeleccionadas.add(letra);
        btn.classList.add('btn-letter-selected');
    });
    document.getElementById('btnEscanear').disabled = false;
}

function seleccionarNinguno() {
    document.querySelectorAll('.btn-letter').forEach(function(btn) {
        letrasSeleccionadas.delete(btn.getAttribute('data-letra'));
        btn.classList.remove('btn-letter-selected');
    });
    document.getElementById('btnEscanear').disabled = true;
    document.getElementById('scanResult').style.display = 'none';
    document.getElementById('startSection').style.display = 'none';
}

// ---- Escaneo ----
function escanear() {
    const btn = document.getElementById('btnEscanear');
    btn.disabled = true;
    btn.textContent = 'Escaneando...';

    const fd = new FormData();
    fd.append('accion', 'escanear');
    fd.append('token', token);
    fd.append('usarExclusiones', document.getElementById('chkExcl').checked ? '1' : '0');
    letrasSeleccionadas.forEach(function(l) { fd.append('letras[]', l); });

    fetch('copiar.php', { method: 'POST', body: fd })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            btn.disabled = false;
            btn.textContent = 'ESCANEAR SISTEMAS';

            if (data.error) {
                alert('Error: ' + data.error);
                return;
            }

            // Actualizar info de la fuente
            if (data.source) {
                updateSourceInfo(data.source);
            }

            sistemas = data.sistemas;
            const totalSistemas = data.total;
            const excluidos = data.excluidos;

            // Verificar fuente lista
            if (data.source && !data.source.ready) {
                let html = '<div class="error-box">';
                html += '<h2 style="color: #ff6b6b;">Fuente no disponible</h2>';
                html += '<p>Coloque <strong>sistema.zip</strong> en la misma carpeta que este script,</p>';
                html += '<p>o coloque los archivos ya extraidos en la carpeta <strong>sistema/</strong></p>';
                html += '</div>';
                document.getElementById('scanResult').innerHTML = html;
                document.getElementById('scanResult').style.display = 'block';
                document.getElementById('startSection').style.display = 'none';
                return;
            }

            // Mostrar resultado
            let html = '<div class="scan-result">';
            html += '<h2 style="margin-top:0;">Resultado del Escaneo</h2>';
            html += '<p><strong style="color:#74b9ff;">' + totalSistemas + '</strong> sistemas encontrados para distribucion';
            if (excluidos > 0) {
                html += ' (<strong style="color:#ffd93d;">' + excluidos + '</strong> excluidos)';
            }
            if (data.source) {
                html += '<br><span style="color:#888; font-size:0.85em;">' + escapeHtml(data.source.fileCount.toLocaleString()) + ' archivos por copiar a cada sitio (' + escapeHtml(data.source.sizeHuman) + ')</span>';
            }
            html += '</p>';

            if (totalSistemas > 0) {
                // Agrupar por letra
                const grouped = {};
                sistemas.forEach(function(s) {
                    if (!grouped[s.letra]) grouped[s.letra] = [];
                    grouped[s.letra].push(s.nombre);
                });

                html += '<div class="scan-list">';
                Object.keys(grouped).sort().forEach(function(letra) {
                    grouped[letra].forEach(function(nombre) {
                        html += '<div class="scan-item"><span class="letra-tag">' + escapeHtml(letra) + '</span> ' + escapeHtml(nombre) + '</div>';
                    });
                });
                html += '</div>';
            }

            // Excluidos
            if (data.excluidos_lista && data.excluidos_lista.length > 0) {
                html += '<details style="margin-top:10px;"><summary style="color:#ffd93d;cursor:pointer;">Ver excluidos (' + excluidos + ')</summary>';
                html += '<div class="scan-list" style="max-height:150px;">';
                data.excluidos_lista.forEach(function(e) {
                    html += '<div class="scan-item" style="color:#ffd93d;">' + escapeHtml(e.nombre) + ' (' + escapeHtml(e.motivo) + ')</div>';
                });
                html += '</div></details>';
            }

            html += '</div>';
            document.getElementById('scanResult').innerHTML = html;
            document.getElementById('scanResult').style.display = 'block';

            // Mostrar boton de iniciar si hay sistemas
            if (totalSistemas > 0) {
                document.getElementById('startSection').style.display = 'block';
            } else {
                document.getElementById('startSection').style.display = 'none';
            }
        })
        .catch(function(e) {
            btn.disabled = false;
            btn.textContent = 'ESCANEAR SISTEMAS';
            alert('Error de conexion: ' + e.message);
        });
}

// ---- Distribucion ----
async function iniciarDistribucion() {
    // === PROTECCION ANTI-DOBLE-EJECUCION ===
    if (isRunning) {
        addLog('BLOQUEADO: Ya hay una distribucion en curso. Ignorando clic duplicado.', 'skip');
        return;
    }

    // Doble confirmacion
    if (!confirm('ATENCION: Se van a COPIAR archivos a ' + sistemas.length + ' sistemas.\n\nLos archivos existentes seran sobrescritos.\n\nContinuar?')) {
        return;
    }

    const typed = prompt('SEGUNDA CONFIRMACION\n\nEscriba COPIAR para continuar:');
    if (typed !== 'COPIAR') {
        alert('Distribucion cancelada. Debe escribir exactamente "COPIAR".');
        return;
    }

    // === MARCAR COMO EN EJECUCION ===
    isRunning = true;

    // Deshabilitar boton inmediatamente para evitar doble clic
    const btnIniciar = document.getElementById('btnIniciar');
    if (btnIniciar) btnIniciar.disabled = true;

    // Iniciar
    cancelled = false;
    currentIndex = 0;
    totalStats = { archivos: 0, bytes: 0, errores: 0 };

    // Congelar la lista de sistemas para esta ejecucion (copia local)
    const sistemasToProcess = sistemas.slice();
    const totalToProcess = sistemasToProcess.length;

    showSection('secProceso');
    document.getElementById('statTotal').textContent = totalToProcess;
    document.getElementById('logBox').innerHTML = '';
    addLog('Iniciando distribucion a ' + totalToProcess + ' sistemas...', 'info');
    addLog('Letras seleccionadas: ' + Array.from(letrasSeleccionadas).sort().join(', '), 'info');
    addLog('Exclusiones: ' + (document.getElementById('chkExcl').checked ? 'SI' : 'NO'), 'info');
    addLog('---', 'info');

    for (let i = 0; i < totalToProcess; i++) {
        if (cancelled) {
            addLog('CANCELADO por el usuario en sistema #' + (i + 1), 'skip');
            break;
        }

        currentIndex = i;
        const sistema = sistemasToProcess[i];

        // Actualizar UI
        updateProgress(i, totalToProcess);
        document.getElementById('currentSystem').style.display = 'block';
        document.getElementById('currentName').textContent = 'Copiando a: ' + sistema.nombre + ' (' + (i + 1) + '/' + totalToProcess + ')...';

        try {
            const fd = new FormData();
            fd.append('accion', 'copiar');
            fd.append('token', token);
            fd.append('sitePath', sistema.path);

            const resp = await fetch('copiar.php', { method: 'POST', body: fd });
            const data = await resp.json();

            if (data.error) {
                addLog('ERROR: ' + sistema.nombre + ' - ' + data.error, 'err');
                totalStats.errores++;
            } else {
                totalStats.archivos += data.archivos;
                totalStats.bytes += data.bytes;

                const errCount = data.errores ? data.errores.length : 0;
                totalStats.errores += errCount;

                let logClass = errCount > 0 ? 'skip' : 'ok';
                addLog(
                    (logClass === 'ok' ? 'OK' : 'PARCIAL') + ': ' + sistema.nombre +
                    ' | ' + data.archivos + ' archivos (' + data.bytesHuman + ')' +
                    (errCount > 0 ? ' ' + errCount + ' errores' : '') +
                    ' en ' + data.tiempo + 's',
                    logClass
                );

                // Mostrar errores de copia si los hay
                if (data.errores && data.errores.length > 0) {
                    data.errores.slice(0, 5).forEach(function(e) {
                        addLog('  ' + e, 'err');
                    });
                    if (data.errores.length > 5) {
                        addLog('  ... y ' + (data.errores.length - 5) + ' errores mas', 'err');
                    }
                }
            }
        } catch(e) {
            addLog('FETCH ERROR: ' + sistema.nombre + ' - ' + e.message, 'err');
            totalStats.errores++;
        }

        // Actualizar contadores
        document.getElementById('statProcesados').textContent = i + 1;
        document.getElementById('statArchivos').textContent = totalStats.archivos.toLocaleString();
        document.getElementById('statBytes').textContent = formatBytesJS(totalStats.bytes);
        document.getElementById('statErrores').textContent = totalStats.errores;
    }

    // === COMPLETAR Y LIBERAR ===
    updateProgress(totalToProcess, totalToProcess);
    document.getElementById('currentSystem').style.display = 'none';
    document.getElementById('btnCancelar').disabled = true;

    addLog('---', 'info');
    addLog('DISTRIBUCION FINALIZADA. ' + totalToProcess + ' sistemas procesados. ' + totalStats.archivos.toLocaleString() + ' archivos copiados.', 'info');

    // Liberar bandera
    isRunning = false;

    // Re-habilitar boton
    if (btnIniciar) btnIniciar.disabled = false;

    // Mostrar resumen
    showResumen(cancelled);
}

function cancelar() {
    if (confirm('Cancelar la distribucion? El sistema actual terminara de procesarse.')) {
        cancelled = true;
        addLog('Cancelacion solicitada. Esperando a que termine el sistema actual...', 'skip');
        document.getElementById('btnCancelar').disabled = true;
        document.getElementById('btnCancelar').textContent = 'CANCELANDO...';
    }
}

// ---- UI helpers ----
function updateProgress(current, total) {
    const pct = total > 0 ? Math.round((current / total) * 100) : 0;
    const fill = document.getElementById('progressFill');
    fill.style.width = pct + '%';
    fill.textContent = pct + '% (' + current + '/' + total + ')';
}

function addLog(message, type) {
    const box = document.getElementById('logBox');
    const div = document.createElement('div');
    div.className = 'log-' + (type || 'info');
    div.textContent = message;
    box.appendChild(div);
    box.scrollTop = box.scrollHeight;
}

function showResumen(wasCancelled) {
    // Copiar log
    document.getElementById('logFinal').innerHTML = document.getElementById('logBox').innerHTML;

    // Titulo
    const box = document.getElementById('resumenBox');
    const titulo = document.getElementById('resumenTitulo');
    const texto = document.getElementById('resumenTexto');

    if (wasCancelled) {
        box.className = 'complete cancelled';
        titulo.textContent = 'DISTRIBUCION CANCELADA';
        texto.textContent = 'Se procesaron ' + currentIndex + ' de ' + sistemas.length + ' sistemas antes de cancelar.';
    } else if (totalStats.errores > 0) {
        box.className = 'complete';
        box.style.borderColor = '#ffd93d';
        box.style.background = '#3d3d00';
        titulo.style.color = '#ffd93d';
        titulo.textContent = 'DISTRIBUCION COMPLETADA CON ADVERTENCIAS';
        texto.textContent = 'Se procesaron ' + sistemas.length + ' sistemas. ' + totalStats.errores + ' errores encontrados.';
    } else {
        box.className = 'complete';
        titulo.textContent = 'DISTRIBUCION COMPLETADA';
        texto.textContent = 'Se distribuyeron archivos exitosamente a ' + sistemas.length + ' sistemas.';
    }

    // Stats del resumen
    const statsHtml = '<div class="stats">' +
        '<div class="stat-box"><div class="stat-value val-blue">' + sistemas.length + '</div><div class="stat-label">Sistemas procesados</div></div>' +
        '<div class="stat-box"><div class="stat-value val-purple">' + totalStats.archivos.toLocaleString() + '</div><div class="stat-label">Archivos copiados</div></div>' +
        '<div class="stat-box"><div class="stat-value val-green">' + formatBytesJS(totalStats.bytes) + '</div><div class="stat-label">Total distribuido</div></div>' +
        '<div class="stat-box"><div class="stat-value val-red">' + totalStats.errores + '</div><div class="stat-label">Errores</div></div>' +
        '</div>';
    document.getElementById('resumenStats').innerHTML = statsHtml;

    showSection('secCompleto');
}

function nuevaDistribucion() {
    if (isRunning) {
        alert('Hay una distribucion en curso. Espere a que termine o cancele primero.');
        return;
    }
    sistemas = [];
    currentIndex = 0;
    cancelled = false;
    isRunning = false;
    totalStats = { archivos: 0, bytes: 0, errores: 0 };
    document.getElementById('btnCancelar').disabled = false;
    var btnIniciar = document.getElementById('btnIniciar');
    if (btnIniciar) btnIniciar.disabled = false;
    document.getElementById('scanResult').style.display = 'none';
    document.getElementById('startSection').style.display = 'none';
    showSection('secLetras');
}

function resetProgreso() {
    if (!confirm('Reiniciar el progreso? Se borra el historial de distribuciones anteriores.')) return;

    const fd = new FormData();
    fd.append('accion', 'reset');
    fd.append('token', token);

    fetch('copiar.php', { method: 'POST', body: fd })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            if (data.ok) alert('Progreso reiniciado');
            else alert('Error: ' + (data.error || 'desconocido'));
        })
        .catch(function(e) { alert('Error: ' + e.message); });
}
</script>
</body>
</html>
