<?php
require_once __DIR__ . '/config.php';

requireLicense();

// ── RATE LIMITING: máx 10 intentos por IP por minuto ─────────────────────────
function checkRateLimit(PDO $db, string $ip): void {
    // Aseguramos que existe la tabla
    $db->exec("CREATE TABLE IF NOT EXISTS portal_rate_limit (
        ip       VARCHAR(45)  NOT NULL,
        hits     INT          NOT NULL DEFAULT 1,
        window   INT          NOT NULL,
        PRIMARY KEY (ip, window)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

    $window = (int)(time() / 60); // ventana de 1 minuto

    // Upsert: incrementar contador
    $db->prepare(
        "INSERT INTO portal_rate_limit (ip, hits, window)
         VALUES (?, 1, ?)
         ON DUPLICATE KEY UPDATE hits = hits + 1"
    )->execute([$ip, $window]);

    // Limpiar ventanas antiguas (> 5 min)
    $db->prepare(
        "DELETE FROM portal_rate_limit WHERE window < ?"
    )->execute([$window - 5]);

    // Leer contador actual
    $st = $db->prepare(
        "SELECT hits FROM portal_rate_limit WHERE ip=? AND window=?"
    );
    $st->execute([$ip, $window]);
    $row = $st->fetch();
    $hits = $row ? (int)$row['hits'] : 1;

    if ($hits > 10) {
        http_response_code(429);
        ob_clean();
        echo json_encode(['error' => 'Demasiados intentos. Espere un minuto e intente de nuevo.'], JSON_UNESCAPED_UNICODE);
        exit;
    }
}

// ── TOKEN TEMPORAL (15 minutos) ───────────────────────────────────────────────
function generarTokenPortal(PDO $db, int $patientId): string {
    $db->exec("CREATE TABLE IF NOT EXISTS portal_sessions (
        token      CHAR(64)    NOT NULL PRIMARY KEY,
        patient_id INT         NOT NULL,
        expires_at DATETIME    NOT NULL,
        created_at DATETIME    NOT NULL DEFAULT NOW()
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

    // Limpiar tokens expirados
    $db->exec("DELETE FROM portal_sessions WHERE expires_at < NOW()");

    $token = bin2hex(random_bytes(32));
    $db->prepare(
        "INSERT INTO portal_sessions (token, patient_id, expires_at)
         VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 15 MINUTE))"
    )->execute([$token, $patientId]);

    return $token;
}

function validarTokenPortal(PDO $db, string $token): ?int {
    $db->exec("DELETE FROM portal_sessions WHERE expires_at < NOW()");

    $st = $db->prepare(
        "SELECT patient_id FROM portal_sessions
         WHERE token=? AND expires_at > NOW() LIMIT 1"
    );
    $st->execute([$token]);
    $row = $st->fetch();
    return $row ? (int)$row['patient_id'] : null;
}

// ─────────────────────────────────────────────────────────────────────────────
$action = $_GET['action'] ?? '';
$ip     = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$ip     = trim(explode(',', $ip)[0]); // primer IP si hay proxy

try {
    $db = getDB();

    // ── ACCIÓN: buscar_dni ────────────────────────────────────────────────────
    if ($action === 'buscar_dni') {
        $dni = trim($_GET['dni'] ?? '');
        if (!$dni) jsonError('DNI requerido', 400);

        checkRateLimit($db, $ip);

        $st = $db->prepare(
            "SELECT id, first_name, last_name, dni, dob, phone, sex, address,
                    pathologies, background
             FROM patients
             WHERE dni = ? AND active = 1
             LIMIT 1"
        );
        $st->execute([$dni]);
        $patient = $st->fetch();

        if (!$patient) jsonError('Paciente no encontrado', 404);

        foreach (['pathologies', 'background'] as $f) {
            if (isset($patient[$f]) && is_string($patient[$f])) {
                $patient[$f] = json_decode($patient[$f], true) ?? [];
            }
        }

        // Atenciones cerradas
        $st2 = $db->prepare(
            "SELECT id, date, time, service, attended_by, notes, photos, closed
             FROM attendances
             WHERE patient_id = ? AND closed = 1
             ORDER BY date DESC, time DESC"
        );
        $st2->execute([$patient['id']]);
        $attendances = $st2->fetchAll();

        foreach ($attendances as &$att) {
            if (isset($att['photos']) && is_string($att['photos'])) {
                $att['photos'] = json_decode($att['photos'], true) ?? [];
            }
        }
        unset($att);

        // Generar token temporal
        $token = generarTokenPortal($db, (int)$patient['id']);

        jsonOk([
            'token'       => $token,
            'patient'     => $patient,
            'attendances' => $attendances,
        ]);

    // ── ACCIÓN: ver_historia (con token) ──────────────────────────────────────
    } elseif ($action === 'ver_historia') {
        $token = trim($_GET['token'] ?? '');
        if (!$token) jsonError('Token requerido', 401);

        $patientId = validarTokenPortal($db, $token);
        if (!$patientId) jsonError('Sesión expirada. Escanee el QR nuevamente.', 401);

        $st = $db->prepare(
            "SELECT id, first_name, last_name, dni, dob, phone, sex, address,
                    pathologies, background
             FROM patients WHERE id=? AND active=1 LIMIT 1"
        );
        $st->execute([$patientId]);
        $patient = $st->fetch();
        if (!$patient) jsonError('Paciente no encontrado', 404);

        foreach (['pathologies', 'background'] as $f) {
            if (isset($patient[$f]) && is_string($patient[$f])) {
                $patient[$f] = json_decode($patient[$f], true) ?? [];
            }
        }

        $st2 = $db->prepare(
            "SELECT id, date, time, service, attended_by, notes, photos, closed
             FROM attendances
             WHERE patient_id=? AND closed=1
             ORDER BY date DESC, time DESC"
        );
        $st2->execute([$patientId]);
        $attendances = $st2->fetchAll();

        foreach ($attendances as &$att) {
            if (isset($att['photos']) && is_string($att['photos'])) {
                $att['photos'] = json_decode($att['photos'], true) ?? [];
            }
        }
        unset($att);

        jsonOk(['patient' => $patient, 'attendances' => $attendances]);

    } else {
        jsonError('Acción no válida', 400);
    }

} catch (Throwable $e) {
    jsonError('Error interno: ' . $e->getMessage(), 500);
}
