<?php
/**
 * Sincronizador de EVENTOS Fanatiz para FUTBOL PY.
 * Solo elimina canales creados por este script dentro de FUTBOL PY EVENTOS.
 * NUNCA toca otras categorías.
 * Adaptado para: http://192.168.100.119/fanatiz/
 * ⮕ Agregar SOLO eventos disponibles y eliminar cuando desaparecen.
 */
require_once __DIR__ . '/includes/config.php';

$config = [
    'json_url'  => 'https://yaetv.com/fanatiz/eventos_tigo.json',
    'log_file'  => __DIR__ . '/eventos_fanatiz.log',
    'category_name' => 'FUTBOL PY EVENTOS',
    'timeout' => 60, // Aumentado porque se toma unos segundos
    'max_retries' => 3,
    'retry_delay' => 2, // segundos entre intentos
];

ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(0);

// Conexión centralizada
$pdo = pdo();

// Validar conexión PDO
if (!$pdo) {
    echo "ERROR: No se pudo conectar a la base de datos" . PHP_EOL;
    exit(1);
}

// Log
function writeLog($m, $file) {
    $ts = date('Y-m-d H:i:s');
    $line = "[$ts] $m" . PHP_EOL;
    file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
    echo $line;
}

/**
 * Obtiene JSON de Fanatiz con reintentos automáticos
 * porque la respuesta puede tardar unos segundos
 */
function getEventsFromFanatiz($url, $maxRetries = 3, $retryDelay = 2) {
    $attempt = 0;
    
    while ($attempt < $maxRetries) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => 60,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        ]);
        
        $resp = curl_exec($ch);
        $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($http === 200 && $resp) {
            $arr = json_decode($resp, true);
            if (is_array($arr) && !empty($arr)) {
                return $arr;
            }
        }
        
        $attempt++;
        if ($attempt < $maxRetries) {
            sleep($retryDelay);
        }
    }
    
    return false;
}

// DB helpers
function getOrCreateCategory(PDO $pdo, $name) {
    $st = $pdo->prepare("SELECT id FROM categories WHERE name = ?");
    $st->execute([$name]);
    $id = $st->fetchColumn();
    if (!$id) {
        $st = $pdo->prepare("INSERT INTO categories (name) VALUES (?)");
        $st->execute([$name]);
        $id = $pdo->lastInsertId();
    }
    return (int)$id;
}

function getExistingChannelsByCategory(PDO $pdo, $categoryId) {
    $st = $pdo->prepare("SELECT id, name, uri FROM channels WHERE category_id = ?");
    $st->execute([$categoryId]);
    return $st->fetchAll(PDO::FETCH_ASSOC);
}

/**
 * Sanitiza y formatea el nombre para la BD
 * Elimina tildes/acentos, permite paréntesis y caracteres normales
 */
function sanitizeName($name) {
    // Convertir a UTF-8 si no lo está
    if (!mb_check_encoding($name, 'UTF-8')) {
        $name = mb_convert_encoding($name, 'UTF-8', 'UTF-8');
    }
    
    // Normalizar espacios y trim
    $name = trim($name);
    $name = preg_replace('/\s+/', ' ', $name); // múltiples espacios a uno
    
    // Eliminar tildes/acentos: convertir á->a, é->e, í->i, ó->o, ú->u, ñ->n
    $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
    
    // Eliminar caracteres de control peligrosos
    $name = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $name);
    
    // Convertir a mayúsculas
    $name = mb_strtoupper($name, 'UTF-8');
    
    return $name;
}

/**
 * Verifica si el canal YA existe en la categoría con el mismo nombre exacto
 */
function channelExistsByName(PDO $pdo, $name, $categoryId) {
    $st = $pdo->prepare("SELECT id FROM channels WHERE UPPER(name) = UPPER(?) AND category_id = ?");
    $st->execute([$name, $categoryId]);
    return $st->fetchColumn() !== false;
}

function addChannel(PDO $pdo, array $ev, int $categoryId) {
    // Sanitizar nombre
    $cleanName = sanitizeName($ev['name'] ?? '');
    if (empty($cleanName)) {
        return false; // Nombre vacío después de sanitizar
    }
    
    // Verificar si ya existe con el MISMO nombre exacto
    if (channelExistsByName($pdo, $cleanName, $categoryId)) {
        return false; // Ya existe, no agregar duplicado
    }
    
    // Sanitizar URI
    $cleanUri = trim($ev['uri'] ?? '');
    if (empty($cleanUri)) {
        return false; // URI vacía
    }
    
    // Sanitizar logo
    $cleanLogo = trim($ev['logo'] ?? '');
    
    // Headers con sanitización
    $headers = json_encode($ev['headers'] ?? ['Referer' => 'https://watch.fanatiz.com/channels'], JSON_UNESCAPED_SLASHES);
    if ($headers === false) {
        return false;
    }
    
    $st = $pdo->prepare("
        INSERT INTO channels (name, uri, category_id, logo, headers, webview, isMagis, adultos)
        VALUES (?, ?, ?, ?, ?, 'no', 0, 0)
    ");
    
    return $st->execute([
        $cleanName,
        $cleanUri,
        $categoryId,
        $cleanLogo,
        $headers
    ]);
}

/**
 * Borrado SEGURO: solo elimina dentro de la categoría específica
 * Identifica canales como parte de este sync por la referencia fanatiz en headers
 */
function safeDeleteChannelById(PDO $pdo, int $channelId, int $categoryId) {
    $st = $pdo->prepare("
        DELETE FROM channels
        WHERE id = ?
          AND category_id = ?
          AND webview = 'no'
          AND (headers LIKE '%fanatiz%' OR headers LIKE '%watch.fanatiz%')
    ");
    return $st->execute([$channelId, $categoryId]);
}

/**
 * Limpia canales que desaparecieron del JSON en la categoría específica
 * CRÍTICO: nunca toca otras categorías
 */
function cleanupMissingChannels(PDO $pdo, int $categoryId) {
    $st = $pdo->prepare("
        DELETE FROM channels
        WHERE category_id = ?
          AND webview = 'no'
          AND (headers LIKE '%fanatiz%' OR headers LIKE '%watch.fanatiz%')
    ");
    return $st->execute([$categoryId]);
}

function cleanOldLogs($logFile, $daysToKeep = 7) {
    if (!file_exists($logFile)) return;
    $lines = file($logFile, FILE_IGNORE_NEW_LINES);
    $cutoff = date('Y-m-d', strtotime("-$daysToKeep days"));
    $out = [];
    foreach ($lines as $line) {
        if (preg_match('/^\[(\d{4}-\d{2}-\d{2})/', $line, $m)) {
            if ($m[1] >= $cutoff) $out[] = $line;
        }
    }
    if (count($out) < count($lines)) {
        file_put_contents($logFile, implode(PHP_EOL, $out) . PHP_EOL);
        writeLog("Logs antiguos limpiados, mantenidas " . count($out) . " líneas", $logFile);
    }
}

// --- Principal ---
try {
    cleanOldLogs($config['log_file'], 3);

    writeLog("=== Sync EVENTOS FANATIZ (FUTBOL PY) ===", $config['log_file']);
    writeLog("Conectando a: " . $config['json_url'] . " (timeout: " . $config['timeout'] . "s)", $config['log_file']);
    
    $events = getEventsFromFanatiz($config['json_url'], $config['max_retries'], $config['retry_delay']);
    
    if ($events === false || !is_array($events)) {
        writeLog("ERROR: No se pudo obtener los eventos de Fanatiz después de " . $config['max_retries'] . " intentos", $config['log_file']);
        exit(1);
    }
    
    if (empty($events)) {
        writeLog("ADVERTENCIA: JSON recibido pero está vacío", $config['log_file']);
        exit(0);
    }
    
    writeLog("✓ JSON ok. Eventos recibidos: " . count($events), $config['log_file']);

    // Validar y normalizar eventos
    $current = [];
    $valid = 0;
    
    foreach ($events as $ev) {
        if (empty($ev['name']) || empty($ev['uri'])) {
            writeLog("⚠ Evento sin name o uri, ignorado", $config['log_file']);
            continue;
        }
        
        // Log del evento RAW
        writeLog("  DEBUG: Evento raw name='" . $ev['name'] . "'", $config['log_file']);
        
        // Sanitizar nombre
        $cleanName = sanitizeName($ev['name']);
        
        // Log del nombre sanitizado
        writeLog("  DEBUG: Después sanitizar='" . $cleanName . "'", $config['log_file']);
        
        if (empty($cleanName)) {
            writeLog("⚠ Evento con nombre inválido/vacío después de sanitizar, ignorado", $config['log_file']);
            continue;
        }
        
        $k = $cleanName . '|' . trim($ev['uri']);
        
        // Evitar duplicados en el array (mismo name)
        if (isset($current[$k])) {
            writeLog("⚠ Evento duplicado en JSON: " . $cleanName, $config['log_file']);
            continue;
        }
        
        $current[$k] = $ev;
        $valid++;
    }
    
    writeLog("✓ Eventos validados: " . $valid, $config['log_file']);

    // Procesamiento de categoría
    $categoryName = $config['category_name'];
    writeLog("--- Categoría: " . $categoryName . " ---", $config['log_file']);
    
    $catId = getOrCreateCategory($pdo, $categoryName);
    writeLog("✓ Category ID: " . $catId, $config['log_file']);

    // Existentes en DB SOLO en esta categoría (solo los marcados por este script)
    $existing = getExistingChannelsByCategory($pdo, $catId);
    $existingMap = [];
    foreach ($existing as $ch) {
        $k = $ch['name'] . '|' . $ch['uri'];
        $existingMap[$k] = $ch;
    }

    // Diferencias
    $toAdd = array_diff_key($current, $existingMap);
    $toDel = array_diff_key($existingMap, $current);

    writeLog("→ A agregar: " . count($toAdd) . ", A eliminar: " . count($toDel), $config['log_file']);

    // Iniciar transacción
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    $pdo->beginTransaction();
    
    $added = 0; 
    $addErr = 0; 
    $deleted = 0; 
    $delErr = 0;

    // Agregar nuevos eventos
    foreach ($toAdd as $ev) {
        $cleanName = sanitizeName($ev['name']);
        writeLog("  [+] " . $cleanName, $config['log_file']);
        if (addChannel($pdo, $ev, $catId)) {
            $added++;
        } else {
            $addErr++;
            writeLog("    ✗ Error al agregar o ya existe", $config['log_file']);
        }
    }

    // Borrado SEGURO: respeta categoría y marca (solo FUTBOL PY EVENTOS)
    foreach ($toDel as $ch) {
        writeLog("  [-] " . $ch['name'], $config['log_file']);
        if (safeDeleteChannelById($pdo, (int)$ch['id'], $catId)) {
            $deleted++;
        } else {
            $delErr++;
            writeLog("    ✗ Error/omitido (no cumple marca o categoría)", $config['log_file']);
        }
    }

    // Commit o Rollback
    if ($addErr === 0 && $delErr === 0) {
        $pdo->commit();
        writeLog("✓ Commit " . $categoryName, $config['log_file']);
    } else {
        if (($addErr + $delErr) < max(1, (count($toAdd) + count($toDel)) / 2)) {
            $pdo->commit();
            writeLog("⚠ Commit parcial " . $categoryName, $config['log_file']);
        } else {
            $pdo->rollBack();
            writeLog("✗ Rollback " . $categoryName, $config['log_file']);
        }
    }

    $active = count($current);
    
    writeLog("=== FIN SYNC ===", $config['log_file']);
    writeLog("📊 RESUMEN → +" . $added . " (err " . $addErr . ") | -" . $deleted . " (err " . $delErr . ") | Activos " . $active, $config['log_file']);
    exit(0);

} catch (Throwable $e) {
    if ($pdo && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    writeLog("💥 EXCEPCIÓN: " . $e->getMessage(), $config['log_file']);
    writeLog($e->getTraceAsString(), $config['log_file']);
    exit(1);
}