<?php
/*
 * Paginación automática de diarios - componente independiente.
 * NO modifica ni utiliza el OCR interactivo de Noticias.
 */
require_once __DIR__ . '/gestor_archivos.php';
require_once __DIR__ . '/optimizador_multimedia.php';

if (!function_exists('ec_paginacion_preparar_esquema')) {
function ec_paginacion_preparar_esquema(mysqli $conn): void {
    ec_gestor_preparar_esquema($conn);

    $cols = [];
    if ($rs = $conn->query("SHOW COLUMNS FROM archivos_clasificacion")) {
        while ($r = $rs->fetch_assoc()) $cols[$r['Field']] = true;
        $rs->free();
    }
    $adds = [
        'pagina_editorial' => "ALTER TABLE archivos_clasificacion ADD COLUMN pagina_editorial INT NULL AFTER consecutivo",
        'lote_carga'       => "ALTER TABLE archivos_clasificacion ADD COLUMN lote_carga VARCHAR(80) NULL AFTER pagina_editorial",
        'posicion_lote'    => "ALTER TABLE archivos_clasificacion ADD COLUMN posicion_lote INT NULL AFTER lote_carga",
        'pagina_edicion'   => "ALTER TABLE archivos_clasificacion ADD COLUMN pagina_edicion INT NULL AFTER posicion_lote",
        'tipo_pagina'      => "ALTER TABLE archivos_clasificacion ADD COLUMN tipo_pagina VARCHAR(20) NULL AFTER pagina_edicion",
        'lote_origen'      => "ALTER TABLE archivos_clasificacion ADD COLUMN lote_origen VARCHAR(64) NULL AFTER tipo_pagina",
        'fuente_hash'      => "ALTER TABLE archivos_clasificacion ADD COLUMN fuente_hash CHAR(64) NULL AFTER lote_origen",
    ];
    foreach ($adds as $c => $sql) {
        if (!isset($cols[$c])) $conn->query($sql);
    }

    /* El mismo número de página se repite cada día: nombre_tecnico NO puede ser UNIQUE. */
    if ($rs = $conn->query("SHOW INDEX FROM archivos_clasificacion WHERE Key_name='uq_nombre_tecnico'")) {
        if ($rs->num_rows > 0) $conn->query("ALTER TABLE archivos_clasificacion DROP INDEX uq_nombre_tecnico");
        $rs->free();
    }
    if ($rs = $conn->query("SHOW INDEX FROM archivos_clasificacion WHERE Key_name='idx_nombre_tecnico'")) {
        if ($rs->num_rows === 0) $conn->query("CREATE INDEX idx_nombre_tecnico ON archivos_clasificacion(nombre_tecnico)");
        $rs->free();
    }

    $conn->query("CREATE TABLE IF NOT EXISTS archivos_lotes_pdf (
        hash_pdf CHAR(64) NOT NULL PRIMARY KEY,
        nombre_original VARCHAR(255) NOT NULL,
        medio_id INT NOT NULL,
        paginas INT NOT NULL DEFAULT 0,
        usuario_id INT NOT NULL,
        creado_en DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
}

function ec_paginacion_url(): string {
    $f = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'paginacion_config' . DIRECTORY_SEPARATOR . 'paginacion_url.txt';
    if (!is_file($f) || !is_readable($f)) return '';

    $u = rtrim(trim((string)file_get_contents($f)), '/');
    if ($u === '') return '';

    /*
     * PRUEBA LOCALHOST:
     * PHP (Laragon) y PAGINACION_SERVER están en la misma PC.
     * Se permite 127.0.0.1:8010 únicamente cuando la propia
     * plataforma también se está ejecutando en localhost.
     */
    $host = strtolower((string)($_SERVER['HTTP_HOST'] ?? ''));
    $esLocal = (
        $host === 'localhost' ||
        strpos($host, 'localhost:') === 0 ||
        $host === '127.0.0.1' ||
        strpos($host, '127.0.0.1:') === 0
    );

    if ($esLocal && preg_match('~^http://(?:127\.0\.0\.1|localhost):8010$~i', $u)) {
        return $u;
    }

    /*
     * PRODUCCIÓN:
     * se conserva exclusivamente la URL HTTPS de Cloudflare.
     */
    if (preg_match('~^https://[a-z0-9-]+\.trycloudflare\.com$~i', $u)) {
        return $u;
    }

    return '';
}

function ec_paginacion_carpeta_usuario(mysqli $conn, int $usuarioId, string $baseDir): array {
    $rol = 'Usuario';
    $login = 'usuario_' . $usuarioId;
    $st = $conn->prepare("SELECT u.usuario, u.rol_id, r.nombre AS rol_nombre FROM usuarios u LEFT JOIN roles r ON r.id=u.rol_id WHERE u.id=? LIMIT 1");
    if ($st) {
        $st->bind_param('i', $usuarioId); $st->execute();
        if ($u = $st->get_result()->fetch_assoc()) {
            $login = (string)($u['usuario'] ?: $login);
            $rid = (int)($u['rol_id'] ?? 0);
            if ($rid === 1) $rol = 'Superadministrador';
            elseif ($rid === 2) $rol = 'Administrador';
            elseif ($rid === 3) $rol = 'Editor';
            elseif (!empty($u['rol_nombre'])) $rol = (string)$u['rol_nombre'];
        }
        $st->close();
    }
    $limpiar = static function(string $v): string {
        $v = trim($v);
        $v = preg_replace('/[^A-Za-z0-9._-]+/u', '_', $v);
        return trim((string)$v, '._-') ?: 'usuario';
    };
    $rel = $limpiar($rol) . '/' . $limpiar($login) . '/' . date('Y') . '/' . date('m') . '/' . date('d') . '/';
    $dir = rtrim($baseDir, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $rel);
    if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) throw new RuntimeException('No se pudo crear la carpeta de destino.');
    return ['dir' => rtrim($dir, '/\\') . DIRECTORY_SEPARATOR, 'rel' => $rel];
}

function ec_paginacion_datos_clasificacion(mysqli $conn, string $region, string $tipo, int $medioId): array {
    $codigoTipo = ec_gestor_codigo_tipo($tipo);
    if ($codigoTipo !== 'DR') throw new RuntimeException('La paginación automática solo se aplica al tipo Diario/Prensa.');
    $st = $conn->prepare("SELECT codigo FROM regiones_codigos WHERE region=? LIMIT 1");
    $st->bind_param('s', $region); $st->execute();
    $codigoRegion = (string)($st->get_result()->fetch_assoc()['codigo'] ?? ''); $st->close();
    if ($codigoRegion === '') throw new RuntimeException('La región seleccionada no tiene código.');
    $st = $conn->prepare("SELECT id,nombre_medio FROM medios WHERE id=? AND estado='activo' LIMIT 1");
    $st->bind_param('i', $medioId); $st->execute(); $medio = $st->get_result()->fetch_assoc(); $st->close();
    if (!$medio) throw new RuntimeException('El medio no existe o está inactivo.');
    return [
        'codigo_tipo' => $codigoTipo,
        'codigo_region' => $codigoRegion,
        'medio_nombre' => (string)$medio['nombre_medio'],
        'slug' => ec_gestor_slug_medio((string)$medio['nombre_medio'], $codigoTipo),
    ];
}

function ec_paginacion_registrar_jpg(mysqli $conn, int $usuarioId, string $baseDir, array $clasif, string $region, string $tipo, int $medioId, string $original, string $jpgBytes, int $pagina, string $tipoPagina, string $lote, int $posicion, string $fuenteHash=''): array {
    ec_paginacion_preparar_esquema($conn);
    if ($jpgBytes === '') throw new RuntimeException('La página procesada llegó vacía.');

    $hash = hash('sha256', $jpgBytes);
    $st = $conn->prepare("SELECT ac.nombre_tecnico FROM archivos_clasificacion ac INNER JOIN archivos a ON a.id=ac.archivo_id WHERE ac.hash_sha256=? AND ac.region=? AND ac.medio_id=? LIMIT 1");
    $st->bind_param('ssi', $hash, $region, $medioId); $st->execute(); $dup = $st->get_result()->fetch_assoc(); $st->close();
    if ($dup) return ['ok'=>false, 'duplicado'=>true, 'mensaje'=>'Página duplicada: ' . $dup['nombre_tecnico']];

    $numero = str_pad((string)max(0, $pagina), 4, '0', STR_PAD_LEFT);
    $sufijo = $numero;
    if ($tipoPagina === 'PORTADA') $sufijo = '0000_PORTADA';
    elseif ($tipoPagina === 'CONTRAPORTADA') $sufijo = $numero . '_CONTRAPORTADA';

    $nombreBase = $clasif['codigo_region'] . '_DR_' . $clasif['slug'] . '_' . $sufijo;
    $nombreTecnico = $nombreBase . '.JPG';

    $up = ec_paginacion_carpeta_usuario($conn, $usuarioId, $baseDir);
    $ruta = $up['dir'] . $nombreTecnico;
    $relativo = $up['rel'] . $nombreTecnico;

    if (is_file($ruta)) throw new RuntimeException('Ya existe ' . $nombreTecnico . ' en la carpeta de hoy.');
    if (file_put_contents($ruta, $jpgBytes, LOCK_EX) === false) throw new RuntimeException('No se pudo guardar la página JPG.');

    /* El reconocimiento se hizo ANTES. Aquí sólo se aplica el optimizador existente. */
    if (function_exists('ec_optimizar_archivo_subido')) ec_optimizar_archivo_subido($ruta, $original);

    $hashFinal = hash_file('sha256', $ruta);
    $fecha = date('Y-m-d');
    $descripcion = 'Página de diario procesada automáticamente.';
    $paginaEdicion = $pagina;
    $consecutivo = max(0, $pagina);
    $loteOrigen = substr(hash('sha256', $lote . '|' . $original), 0, 64);

    $conn->begin_transaction();
    try {
        $st = $conn->prepare("INSERT INTO archivos(usuario_id,nota_origen_id,titulo,descripcion,archivo,tipo_archivo,fecha_archivo,estado) VALUES(?,NULL,?,?,?,?,?,'activo')");
        $tipoArchivo = 'imagen';
        $st->bind_param('isssss', $usuarioId, $nombreBase, $descripcion, $relativo, $tipoArchivo, $fecha);
        $st->execute(); $archivoId = (int)$conn->insert_id; $st->close();

        $medioNombre = $clasif['medio_nombre'];
        $codigoRegion = $clasif['codigo_region']; $codigoTipo = 'DR';
        $fuenteHash = trim($fuenteHash);
        $st = $conn->prepare("INSERT INTO archivos_clasificacion(archivo_id,nombre_original,nombre_tecnico,hash_sha256,region,codigo_region,tipo_medio,codigo_tipo,medio_id,medio_nombre,consecutivo,pagina_editorial,lote_carga,posicion_lote,pagina_edicion,tipo_pagina,lote_origen,fuente_hash,usuario_id) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
        $st->bind_param('isssssssisiiiisissi', $archivoId, $original, $nombreTecnico, $hashFinal, $region, $codigoRegion, $tipo, $codigoTipo, $medioId, $medioNombre, $consecutivo, $pagina, $lote, $posicion, $paginaEdicion, $tipoPagina, $loteOrigen, $fuenteHash, $usuarioId);
        $st->execute(); $st->close();
        $conn->commit();
        return ['ok'=>true,'id'=>$archivoId,'nombre_tecnico'=>$nombreTecnico,'archivo'=>$relativo,'pagina'=>$pagina,'tipo_pagina'=>$tipoPagina];
    } catch (Throwable $e) {
        $conn->rollback(); @unlink($ruta); throw $e;
    }
}

function ec_paginacion_numero_nombre(string $nombre): int {
    /*
     * PRODUCCIÓN V4:
     * El nombre de origen es sólo metadato/auditoría.
     * NUNCA determina el número editorial.
     */
    return 0;
}

function ec_paginacion_asignar_imagenes(array $paginas): array {
    /*
     * REGLA DE PRODUCCION:
     * - El nombre original NO participa en la paginacion.
     * - Solo usamos la lectura de la imagen (OCR/analizador).
     * - Una deteccion repetida (por ejemplo el "4" de "4 de agosto") no puede
     *   provocar que varias imagenes intenten guardarse como la misma pagina.
     * - Toda imagen del lote recibe finalmente un numero unico para que una
     *   lectura dudosa nunca bloquee la carga completa.
     */
    $n = count($paginas);
    if ($n === 0) return [];

    $asignadas = array_fill(0, $n, null);
    $conf = array_fill(0, $n, 0.0);

    /* 1. Solo OCR. El nombre del archivo se ignora por completo. */
    foreach ($paginas as $i => $p) {
        $ocr = (int)($p['detectada'] ?? 0);
        $ocrConf = (float)($p['confianza'] ?? 0);
        if ($ocr >= 1 && $ocr <= 199 && $ocrConf >= 0.55) {
            $asignadas[$i] = $ocr;
            $conf[$i] = $ocrConf;
        }
    }

    /* 2. Si el OCR repite un numero, conservar solo la lectura mas confiable. */
    $mejor = [];
    foreach ($asignadas as $i => $num) {
        if ($num === null) continue;
        if (!isset($mejor[$num]) || $conf[$i] > $conf[$mejor[$num]]) $mejor[$num] = $i;
    }
    foreach ($asignadas as $i => $num) {
        if ($num !== null && ($mejor[$num] ?? $i) !== $i) $asignadas[$i] = null;
    }

    /* 3. Completar huecos coherentes entre dos paginas OCR confiables. */
    $anchors = [];
    foreach ($asignadas as $i => $num) if ($num !== null) $anchors[] = [$i, $num];
    for ($a = 0; $a < count($anchors) - 1; $a++) {
        [$li,$ln] = $anchors[$a]; [$ri,$rn] = $anchors[$a+1];
        if ($rn > $ln && ($rn-$ln) === ($ri-$li)) {
            for ($j=$li+1; $j<$ri; $j++) if ($asignadas[$j] === null) $asignadas[$j] = $ln + ($j-$li);
        }
    }

    /* 4. Garantia de carga: ninguna imagen queda sin pagina y no se repiten numeros. */
    $usados = [];
    foreach ($asignadas as $num) if ($num !== null) $usados[(int)$num] = true;
    $siguiente = 1;
    foreach ($asignadas as $i => $num) {
        if ($num !== null) continue;
        while (isset($usados[$siguiente])) $siguiente++;
        $asignadas[$i] = $siguiente;
        $usados[$siguiente] = true;
        $siguiente++;
    }

    return array_map('intval', $asignadas);
}
}
