<?php
/**
 * Sincronización de query logs desde AdGuard Home.
 * Consulta TODOS los servidores definidos en ADGUARD_SERVERS.
 *
 * Ejecutar via cPanel Cron Jobs cada 1 minuto:
 *   * * * * *   php /home/usuario/public_html/zerotrust/cron.php
 *
 * También puede invocarse via HTTP con token:
 *   https://tu-dominio.com/cron.php?token=CAMBIAR_SECRET_CRON
 */

if (PHP_SAPI !== 'cli') {
    require_once __DIR__ . '/config.php';
    $token = $_GET['token'] ?? '';
    if ($token !== CRON_SECRET) {
        http_response_code(403);
        die('Acceso denegado');
    }
} else {
    require_once __DIR__ . '/config.php';
}

require_once __DIR__ . '/db.php';
require_once __DIR__ . '/helpers.php';

set_time_limit(300);
ini_set('memory_limit', '256M');

$pdo = db();
$log = function(string $msg) {
    $ts = date('[Y-m-d H:i:s]');
    if (PHP_SAPI === 'cli') echo $ts . ' ' . $msg . PHP_EOL;
};

// Agregar columna server_id si no existe (migración automática)
try {
    $pdo->exec("ALTER TABLE query_logs ADD COLUMN server_id VARCHAR(20) NOT NULL DEFAULT 'sn' AFTER record_hash");
    $log("Columna server_id agregada a query_logs.");
} catch (PDOException) {
    // Ya existe, ignorar
}
try {
    $pdo->exec("ALTER TABLE query_logs ADD INDEX idx_server (server_id)");
} catch (PDOException) {}

// Agregar columna server_id a sync_log si no existe
try {
    $pdo->exec("ALTER TABLE sync_log ADD COLUMN server_id VARCHAR(20) NOT NULL DEFAULT 'sn' AFTER id");
} catch (PDOException) {}

$log("=== Iniciando sincronización de " . count(ADGUARD_SERVERS) . " servidor(es) ===");

foreach (ADGUARD_SERVERS as $server) {
    syncServer($pdo, $server, $log);
}

$log("=== Sincronización completada ===");

// ---------------------------------------------------------------
function syncServer(PDO $pdo, array $server, callable $log): void
{
    $serverId   = $server['id'];
    $serverName = $server['name'];

    $log("-- Servidor: $serverName ($serverId) --");

    // Registrar inicio
    $stmt = $pdo->prepare("INSERT INTO sync_log (server_id, status) VALUES (?, 'running')");
    $stmt->execute([$serverId]);
    $syncId = (int)$pdo->lastInsertId();

    $totalFetched = 0;
    $totalNew     = 0;
    $newestTime   = null;
    $oldestTime   = null;

    try {
        $filterCategories = getFilterCategories();

        // Obtener listas de filtros para mapear IDs a nombres
        $filtersData = adguardGet('/control/filtering/status', [], $server);
        $filterNames = [];
        if ($filtersData) {
            foreach (array_merge($filtersData['filters'] ?? [], $filtersData['whitelist_filters'] ?? []) as $f) {
                $filterNames[$f['id']] = $f['name'];
            }
        }

        // Cursor por servidor: state_key = "last_log_time_sn", "last_log_time_ph", etc.
        $stateKey    = 'last_log_time_' . $serverId;
        $lastSeenRow = $pdo->prepare("SELECT state_value FROM sync_state WHERE state_key = ?");
        $lastSeenRow->execute([$stateKey]);
        $lastSeenVal = $lastSeenRow->fetchColumn();

        if ($lastSeenVal) {
            $fetchUntil = new DateTime($lastSeenVal);
            $log("  Desde cursor: " . $fetchUntil->format('Y-m-d H:i:s'));
        } else {
            $fetchUntil = new DateTime();
            $fetchUntil->modify('-' . INITIAL_SYNC_HOURS . ' hours');
            $log("  Primera sync, desde: " . $fetchUntil->format('Y-m-d H:i:s'));
        }

        $olderThan = null;
        $pageCount = 0;
        $donePages = false;

        while (!$donePages && $pageCount < SYNC_MAX_PAGES) {
            $params = ['limit' => SYNC_BATCH_SIZE];
            if ($olderThan !== null) {
                $params['older_than'] = $olderThan;
            }

            $data = adguardGet('/control/querylog', $params, $server);
            if ($data === null) {
                $log("  ERROR: No se pudo conectar a {$server['url']} (verifique cURL y conectividad).");
                break;
            }
            if (empty($data['data'])) {
                $log("  Sin más datos.");
                break;
            }

            $records = $data['data'];
            $pageCount++;
            $log("  Página $pageCount: " . count($records) . " registros.");

            $batchNewest = null;
            $batchOldest = null;

            foreach ($records as $record) {
                $totalFetched++;

                $timeStr = $record['time'] ?? '';
                $timeStr = preg_replace('/(\.\d{6})\d*([+-].*)/', '$1$2', $timeStr);
                $logTime = DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $timeStr);
                if (!$logTime) $logTime = new DateTime($timeStr);

                if ($batchNewest === null || $logTime > $batchNewest) $batchNewest = clone $logTime;
                if ($batchOldest === null || $logTime < $batchOldest) $batchOldest = clone $logTime;

                if ($logTime <= $fetchUntil) {
                    $donePages = true;
                }

                $filterId   = isset($record['filterId']) ? (int)$record['filterId'] : null;
                $filterName = $filterId ? ($filterNames[$filterId] ?? null) : null;
                $catInfo    = getCategoryForFilter($filterId);

                $reason = $record['reason'] ?? '';
                if ($reason === 'FilteredSafeBrowsing') {
                    $catInfo = ['name' => 'Navegación Segura', 'color' => '#DC3545', 'icon' => 'bi-shield', 'group' => 'Malware & Amenazas'];
                } elseif ($reason === 'FilteredParental') {
                    $catInfo = ['name' => 'Control Parental', 'color' => '#E91E63', 'icon' => 'bi-person-x', 'group' => 'Contenido Adulto'];
                } elseif (!isBlocked($reason)) {
                    $catInfo = ['name' => 'Permitido', 'color' => '#198754', 'icon' => 'bi-check-circle', 'group' => 'Permitido'];
                }

                $clientId   = $record['client_id'] ?? ($record['client'] ?? '');
                $clientName = $record['client_info']['name'] ?? $clientId;
                $domain     = $record['question']['name'] ?? '';
                $queryType  = $record['question']['type'] ?? 'A';
                $status     = $record['status'] ?? null;
                $elapsedMs  = isset($record['elapsedMs']) ? (float)$record['elapsedMs'] : null;
                $rule       = $record['rules'][0]['text'] ?? ($record['rule'] ?? null);

                // Hash incluye server_id para que el mismo registro en ambos servidores
                // quede guardado por separado (son datos reales de cada nodo)
                $hash = md5($serverId . $clientId . $timeStr . $domain . $queryType . $reason);

                $ins = $pdo->prepare("
                    INSERT IGNORE INTO query_logs
                        (record_hash, server_id, adguard_client_id, client_name, domain, query_type,
                         reason, filter_id, filter_name, category, category_group, category_color,
                         rule_text, status, elapsed_ms, log_time)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ");
                $ins->execute([
                    $hash, $serverId, $clientId, $clientName, $domain, $queryType,
                    $reason, $filterId, $filterName,
                    $catInfo['name'], $catInfo['group'], $catInfo['color'],
                    $rule, $status, $elapsedMs,
                    $logTime->format('Y-m-d H:i:s.v'),
                ]);

                if ($ins->rowCount() > 0) $totalNew++;
            }

            if ($batchNewest && ($newestTime === null || $batchNewest > $newestTime)) $newestTime = clone $batchNewest;
            if ($batchOldest && ($oldestTime === null || $batchOldest < $oldestTime)) $oldestTime = clone $batchOldest;

            if ($batchOldest) {
                $olderThan = $batchOldest->format(DateTime::RFC3339_EXTENDED);
            }

            $log("  Nuevos: $totalNew / Oldest en batch: " . ($batchOldest ? $batchOldest->format('H:i:s') : 'N/A'));
        }

        // Guardar cursor de este servidor
        if ($newestTime) {
            $pdo->prepare("
                INSERT INTO sync_state (state_key, state_value)
                VALUES (?, ?)
                ON DUPLICATE KEY UPDATE state_value = VALUES(state_value)
            ")->execute([$stateKey, $newestTime->format('Y-m-d H:i:s.v')]);
        }

        $pdo->prepare("
            UPDATE sync_log
            SET finished_at = NOW(), records_fetched = ?, records_new = ?,
                oldest_record = ?, newest_record = ?, status = 'ok'
            WHERE id = ?
        ")->execute([
            $totalFetched, $totalNew,
            $oldestTime ? $oldestTime->format('Y-m-d H:i:s') : null,
            $newestTime ? $newestTime->format('Y-m-d H:i:s') : null,
            $syncId,
        ]);

        $log("  OK — Fetched: $totalFetched, Nuevos: $totalNew");

    } catch (Throwable $e) {
        $pdo->prepare("UPDATE sync_log SET finished_at=NOW(), status='error', error_message=? WHERE id=?")
            ->execute([$e->getMessage(), $syncId]);
        $log("  ERROR en $serverName: " . $e->getMessage());
    }
}
