<?php
/**
 * @intranet-file common/enlaces_lib.php
 * @intranet-file-version 2026.03.13.1
 * @intranet-file-date 2026-03-13
 * @intranet-file-owner CSIF Intranet
 */
declare(strict_types=1);

/**
 * Librería común para apps basadas en enlaces JSON (links / nntt / novedades).
 * - Carga/guarda enlaces en JSON con bloqueo.
 * - Agrupa por 'grupo' y ordena de forma estable.
 * - Helpers de seguridad (solo admins para gestión).
 */

function enlaces_is_admin(): bool
{
    $roles = $_SESSION['roles'] ?? [];
    $roles = array_map('strtolower', is_array($roles) ? $roles : []);
    return in_array('administradores', $roles, true);
}

/**
 * Carga enlaces desde JSON.
 * Soporta dos formatos:
 *  - Array simple de enlaces: [ {...}, {...} ]
 *  - Objeto con clave 'links': {"links": [ ... ]}
 */
function enlaces_load(string $jsonFile): array
{
    if (!file_exists($jsonFile)) {
        return [];
    }

    $raw = file_get_contents($jsonFile);
    $data = json_decode((string)$raw, true);
    if (!is_array($data)) {
        return [];
    }

    // Formato nuevo
    if (array_key_exists('links', $data) && is_array($data['links'])) {
        $links = $data['links'];
    } else {
        // Formato antiguo
        $links = $data;
    }

    // Normalizar y añadir posición original para orden estable
    $out = [];
    $i = 0;
    foreach ($links as $row) {
        if (!is_array($row)) {
            $i++;
            continue;
        }
        $out[] = enlaces_normalize_link($row, $i);
        $i++;
    }
    return $out;
}

/**
 * Guarda enlaces en JSON con formato nuevo (objeto con 'links') y pretty print.
 */
function enlaces_save(string $jsonFile, array $links): void
{
    $payload = [
        'version' => 2,
        'updated_at' => date('c'),
        'links' => array_values($links),
    ];

    $json = json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    if ($json === false) {
        throw new RuntimeException('No se pudo codificar el JSON de enlaces.');
    }

    $dir = dirname($jsonFile);
    if (!is_dir($dir)) {
        throw new RuntimeException('Directorio de configuración inexistente: ' . $dir);
    }

    $fp = fopen($jsonFile, 'c+');
    if ($fp === false) {
        throw new RuntimeException('No se pudo abrir el archivo de enlaces para escritura.');
    }

    try {
        if (!flock($fp, LOCK_EX)) {
            throw new RuntimeException('No se pudo bloquear el archivo de enlaces.');
        }
        ftruncate($fp, 0);
        rewind($fp);
        fwrite($fp, $json);
        fflush($fp);
        flock($fp, LOCK_UN);
    } finally {
        fclose($fp);
    }
}

/**
 * Normaliza un enlace, mantiene campos soportados y aplica defaults.
 */
function enlaces_normalize_link(array $in, int $pos = 0): array
{
    $grupo = trim((string)($in['grupo'] ?? 'General'));
    if ($grupo === '') {
        $grupo = 'General';
    }

    $desc = trim((string)($in['descripcion'] ?? ''));
    $dest = trim((string)($in['destino'] ?? ''));
    $img  = trim((string)($in['imagen'] ?? '/images/default.png'));
    $aviso = trim((string)($in['aviso'] ?? ''));

    $orden = $in['orden'] ?? null;
    $orden = is_numeric($orden) ? (int)$orden : null;

    return [
        'grupo' => $grupo,
        'descripcion' => $desc,
        'destino' => $dest,
        'imagen' => $img,
        'aviso' => $aviso,
        'orden' => $orden,
        '_pos' => $pos,
    ];
}

/**
 * Agrupa enlaces por 'grupo' y devuelve:
 *  [
 *    ['name' => 'Grupo', 'links' => [...]],
 *    ...
 *  ]
 * Mantiene orden de grupos por primera aparición.
 */
function enlaces_grouped(array $links): array
{
    $groups = [];
    $groupOrder = [];
    $gpos = 0;

    foreach ($links as $link) {
        if (!is_array($link)) continue;
        $g = (string)($link['grupo'] ?? 'General');
        $g = trim($g) !== '' ? trim($g) : 'General';

        if (!array_key_exists($g, $groups)) {
            $groups[$g] = [];
            $groupOrder[$g] = $gpos++;
        }
        $groups[$g][] = $link;
    }

    // Ordenar enlaces dentro de cada grupo
    foreach ($groups as $g => &$arr) {
        usort($arr, function ($a, $b) {
            $oa = $a['orden'] ?? null;
            $ob = $b['orden'] ?? null;
            if ($oa === null && $ob !== null) return 1;
            if ($oa !== null && $ob === null) return -1;
            if ($oa !== null && $ob !== null && $oa !== $ob) return $oa <=> $ob;
            return ($a['_pos'] ?? 0) <=> ($b['_pos'] ?? 0);
        });
    }
    unset($arr);

    // Orden de grupos: General primero si existe; luego por primera aparición
    $names = array_keys($groups);
    usort($names, function ($a, $b) use ($groupOrder) {
        if ($a === 'General' && $b !== 'General') return -1;
        if ($b === 'General' && $a !== 'General') return 1;
        return ($groupOrder[$a] ?? 0) <=> ($groupOrder[$b] ?? 0);
    });

    $out = [];
    foreach ($names as $name) {
        $out[] = ['name' => $name, 'links' => $groups[$name]];
    }
    return $out;
}

/**
 * CSRF helpers
 */
function enlaces_csrf_token(): string
{
    if (empty($_SESSION['csrf_links'])) {
        $_SESSION['csrf_links'] = bin2hex(random_bytes(16));
    }
    return (string)$_SESSION['csrf_links'];
}

function enlaces_csrf_validate(?string $token): void
{
    $expected = $_SESSION['csrf_links'] ?? '';
    if (!is_string($token) || $token === '' || !hash_equals((string)$expected, $token)) {
        throw new RuntimeException('Token CSRF inválido.');
    }
}

/**
 * Devuelve lista de imágenes disponibles en /styles/images para selector.
 */
function enlaces_list_images(string $stylesImagesDir): array
{
    $files = [];
    if (!is_dir($stylesImagesDir)) {
        return $files;
    }
    foreach (scandir($stylesImagesDir) as $f) {
        if ($f === '.' || $f === '..') continue;
        $path = $stylesImagesDir . DIRECTORY_SEPARATOR . $f;
        if (!is_file($path)) continue;
        if (!preg_match('/\.(png|jpg|jpeg|webp|svg)$/i', $f)) continue;
        $files[] = '/images/' . $f;
    }
    sort($files, SORT_NATURAL | SORT_FLAG_CASE);
    return $files;
}

/**
 * Limpia input de formulario y devuelve un enlace normalizado (sin _pos).
 */
function enlaces_from_post(array $post): array
{
    $link = [
        'grupo' => trim((string)($post['grupo'] ?? 'General')),
        'descripcion' => trim((string)($post['descripcion'] ?? '')),
        'destino' => trim((string)($post['destino'] ?? '')),
        'imagen' => trim((string)($post['imagen'] ?? '/images/default.png')),
        'aviso' => trim((string)($post['aviso'] ?? '')),
        'orden' => isset($post['orden']) && $post['orden'] !== '' ? (int)$post['orden'] : null,
    ];

    // Defaults
    if ($link['grupo'] === '') $link['grupo'] = 'General';
    if ($link['imagen'] === '') $link['imagen'] = '/images/default.png';

    return $link;
}
