<?php
/**
 * Heritage Voice — tour.php
 * API cho chế độ Hướng dẫn viên (Tour Guide Mode)
 *
 * POST   /api/tour/create          Tạo phiên tour (guide)
 * POST   /api/tour/join            Khách join bằng PIN
 * GET    /api/tour/{pin}/state     Poll trạng thái (khách, ~2s/lần)
 * POST   /api/tour/{pin}/command   Gửi lệnh play/pause/seek/next/end (guide)
 * POST   /api/tour/{pin}/ping      Khách báo còn online
 * POST   /api/tour/{pin}/leave     Khách rời phiên
 * GET    /api/tour/{pin}/members   Danh sách khách đang online (guide)
 */

// ── Helpers ───────────────────────────────────────────────────

function tg_gen_pin(): string {
    // 6 chữ số, không bắt đầu bằng 0
    return (string)random_int(100000, 999999);
}

function tg_get_session(string $pin): array {
    $s = db()->prepare("SELECT * FROM tour_sessions WHERE pin=:pin AND status != 'ended'");
    $s->execute([':pin' => $pin]);
    $row = $s->fetch();
    if (!$row) json_error('Phiên tour không tồn tại hoặc đã kết thúc', 404);
    return $row;
}

function tg_require_host(array $session): int {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    if ((int)$session['host_user_id'] !== $uid) json_error('Chỉ hướng dẫn viên mới được thực hiện thao tác này', 403);
    return $uid;
}

function tg_cleanup_stale(string $pin): void {
    // Xoá member không ping trong 15 giây
    db()->prepare("
        UPDATE tour_session_members
        SET left_at = NOW()
        WHERE left_at IS NULL
          AND session_id = (SELECT id FROM tour_sessions WHERE pin=:pin AND status != 'ended')
          AND last_ping < DATE_SUB(NOW(), INTERVAL 15 SECOND)
    ")->execute([':pin' => $pin]);
}

function tg_update_member_count(int $session_id): void {
    db()->prepare("
        UPDATE tour_sessions
        SET member_count = (
            SELECT COUNT(*) FROM tour_session_members
            WHERE session_id=:sid1 AND left_at IS NULL
        )
        WHERE id=:sid2
    ")->execute([':sid1' => $session_id, ':sid2' => $session_id]);
}

// ── 1. Tạo phiên tour ─────────────────────────────────────────

function tg_my_session(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Chưa đăng nhập', 401);
    $s = db()->prepare("
        SELECT ts.pin, ts.status, ts.site_id, ts.current_audio_id,
               ts.playback_state, ts.playback_position, ts.member_count,
               hs.name AS site_name
        FROM tour_sessions ts
        LEFT JOIN heritage_sites hs ON hs.id = ts.site_id
        WHERE ts.host_user_id = :uid AND ts.status IN ('active','waiting')
        ORDER BY ts.created_at DESC LIMIT 1
    ");
    $s->execute([':uid' => $uid]);
    $row = $s->fetch();
    json_ok($row ?: null);
}

function tg_create(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Bạn cần đăng nhập để tạo tour', 401);

    $b = body();
    $site_id = trim($b['site_id'] ?? '');
    if (empty($site_id)) json_error('Thiếu site_id');

    // Kiểm tra site tồn tại
    $s = db()->prepare("SELECT id, name FROM heritage_sites WHERE id=:id AND status='active'");
    $s->execute([':id' => $site_id]);
    $site = $s->fetch();
    if (!$site) json_error('Điểm di sản không tồn tại', 404);

    // Kết thúc mọi session đang active của user này
    db()->prepare("
        UPDATE tour_sessions SET status='ended', ended_at=NOW()
        WHERE host_user_id=:uid AND status != 'ended'
    ")->execute([':uid' => $uid]);

    // Tạo PIN unique
    $pin = tg_gen_pin();
    $attempts = 0;
    while ($attempts < 10) {
        $ck = db()->prepare("SELECT id FROM tour_sessions WHERE pin=:pin AND status != 'ended'");
        $ck->execute([':pin' => $pin]);
        if (!$ck->fetch()) break;
        $pin = tg_gen_pin();
        $attempts++;
    }

    db()->prepare("
        INSERT INTO tour_sessions (pin, host_user_id, site_id, status, created_at)
        VALUES (:pin, :uid, :sid, 'waiting', NOW())
    ")->execute([':pin' => $pin, ':uid' => $uid, ':sid' => $site_id]);

    $session_id = (int)db()->lastInsertId();

    // Lấy danh sách audio points của site
    $ap = db()->prepare("
        SELECT ap.id, ap.title, ap.short_description, ap.duration_sec, ap.order_index,
               ap.audio_url,
               GROUP_CONCAT(al.language_code) AS langs_available
        FROM audio_points ap
        LEFT JOIN audio_localizations al ON al.audio_point_id = ap.id
        WHERE ap.site_id=:sid AND ap.status='active'
        GROUP BY ap.id
        ORDER BY ap.order_index ASC
    ");
    $ap->execute([':sid' => $site_id]);
    $audio_points = $ap->fetchAll();

    json_ok([
        'session_id'   => $session_id,
        'pin'          => $pin,
        'site'         => $site,
        'audio_points' => $audio_points,
    ], 'Đã tạo phiên tour');
}

// ── 2. Khách join bằng PIN ────────────────────────────────────

function tg_join(): void {
    $b    = body();
    $pin  = trim($b['pin'] ?? '');
    $lang = trim($b['lang'] ?? 'vi');

    if (!preg_match('/^\d{6}$/', $pin)) json_error('Mã PIN không hợp lệ');

    $allowed_langs = ['vi','en','zh','ja','ko','fr'];
    if (!in_array($lang, $allowed_langs)) $lang = 'vi';

    $session = tg_get_session($pin);
    tg_cleanup_stale($pin);

    $uid      = get_token_uid();
    $anon_id  = anonymous_id();

    // Kiểm tra đã join chưa
    if ($uid) {
        $ck = db()->prepare("SELECT id FROM tour_session_members WHERE session_id=:sid AND user_id=:uid AND left_at IS NULL");
        $ck->execute([':sid' => $session['id'], ':uid' => $uid]);
    } else {
        $ck = db()->prepare("SELECT id FROM tour_session_members WHERE session_id=:sid AND anonymous_id=:aid AND left_at IS NULL");
        $ck->execute([':sid' => $session['id'], ':aid' => $anon_id]);
    }
    if ($ck->fetch()) {
        // Đã join rồi — cập nhật lang và trả về state luôn
        db()->prepare("UPDATE tour_session_members SET lang=:lang, last_ping=NOW() WHERE session_id=:sid AND " . ($uid ? "user_id=:id" : "anonymous_id=:id"))
            ->execute([':lang' => $lang, ':sid' => $session['id'], ':id' => $uid ?? $anon_id]);
    } else {
        db()->prepare("
            INSERT INTO tour_session_members (session_id, user_id, anonymous_id, lang, joined_at, last_ping)
            VALUES (:sid, :uid, :aid, :lang, NOW(), NOW())
        ")->execute([':sid' => $session['id'], ':uid' => $uid, ':aid' => $anon_id, ':lang' => $lang]);
    }

    tg_update_member_count($session['id']);

    // Lấy audio points với localizations
    $ap = db()->prepare("
        SELECT ap.id, ap.title, ap.short_description, ap.duration_sec, ap.order_index,
               ap.audio_url AS audio_url_vi,
               al.audio_url AS audio_url_localized,
               al.source    AS localization_source
        FROM audio_points ap
        LEFT JOIN audio_localizations al ON al.audio_point_id = ap.id AND al.language_code = :lang1
        WHERE ap.site_id=:sid AND ap.status='active'
        ORDER BY ap.order_index ASC
    ");
    $ap->execute([':sid' => $session['site_id'], ':lang1' => $lang]);
    $audio_points = $ap->fetchAll();

    // Resolve audio url: dùng localized nếu có, fallback vi
    foreach ($audio_points as &$p) {
        $p['audio_url'] = $p['audio_url_localized'] ?? $p['audio_url_vi'];
        $p['is_localized'] = !empty($p['audio_url_localized']);
        unset($p['audio_url_vi'], $p['audio_url_localized'], $p['localization_source']);
    }

    json_ok([
        'session'      => [
            'pin'             => $session['pin'],
            'status'          => $session['status'],
            'playback_state'  => $session['playback_state'],
            'playback_pos'    => (float)$session['playback_position'],
            'current_audio'   => $session['current_audio_id'],
            'member_count'    => $session['member_count'],
        ],
        'lang'         => $lang,
        'audio_points' => $audio_points,
        'anonymous_id' => $anon_id,
    ], 'Đã tham gia phiên tour');
}

// ── 3. Poll trạng thái (khách ~2s/lần) ───────────────────────

function tg_state(string $pin): void {
    $s = db()->prepare("
        SELECT playback_state, playback_position, current_audio_id, status, member_count, last_cmd_at, live_voice_url, webrtc_offer, webrtc_answer
        FROM tour_sessions WHERE pin=:pin
    ");
    $s->execute([':pin' => $pin]);
    $row = $s->fetch();
    if (!$row) json_error('Phiên không tồn tại', 404);
    // Auto-end nếu active quá 30 phút không có hoạt động
    if ($row['status'] === 'active') {
        $lastActivity = strtotime($row['last_cmd_at'] ?? $row['created_at']);
        if (time() - $lastActivity > 1800) {
            db()->prepare("UPDATE tour_sessions SET status='ended', ended_at=NOW() WHERE pin=:p")
               ->execute([':p' => $pin]);
            $row['status'] = 'ended';
        }
    }

    json_ok([
        'status'         => $row['status'],
        'playback_state' => $row['playback_state'],
        'playback_pos'   => (float)$row['playback_position'],
        'current_audio'  => $row['current_audio_id'] ?: null,
        'live_voice_url' => $row['live_voice_url'] ?: null,
        'webrtc_offer'   => $row['webrtc_offer'] ?: null,
        'webrtc_answer'  => $row['webrtc_answer'] ?: null,
        'member_count'   => (int)$row['member_count'],
        'last_cmd_at'    => $row['last_cmd_at'],
    ]);
}

// ── 3b. Guest gửi WebRTC answer ──────────────────────────────────
function tg_answer(string $pin): void {
    $session = tg_get_session($pin);
    if (!$session) json_error('Phiên không tồn tại', 404);
    $b = body();
    $answer = $b['answer'] ?? null;
    if (!$answer) json_error('Thiếu answer');
    db()->prepare("UPDATE tour_sessions SET webrtc_answer=:answer WHERE pin=:pin")
        ->execute([':answer' => $answer, ':pin' => $pin]);
    json_ok(['ok' => true]);
}

// ── 4. Gửi lệnh (guide) ───────────────────────────────────────

function tg_command(string $pin): void {
    $session = tg_get_session($pin);
    tg_require_host($session);

    $b   = body();
    $cmd = trim($b['cmd'] ?? '');  // play | pause | seek | next | end

    $allowed = ['play','pause','seek','next','end','live_voice','webrtc_offer','webrtc_answer','webrtc_stop'];
    if (!in_array($cmd, $allowed)) json_error('Lệnh không hợp lệ');

    $updates  = ['last_cmd_at = NOW()'];
    $params   = [':pin' => $pin];

    match ($cmd) {
        'play' => (function() use (&$updates, &$params, $b, $session) {
            $audio_id = $b['audio_id'] ?? $session['current_audio_id'] ?? null;
            $position = (float)($b['position'] ?? 0);
            $updates[] = "playback_state = 'playing'";
            $updates[] = 'current_audio_id = :audio_id';
            $updates[] = 'playback_position = :pos';
            $updates[] = "status = 'active'";
            $params[':audio_id'] = $audio_id;
            $params[':pos']      = $position;
        })(),
        'pause' => (function() use (&$updates, &$params, $b) {
            $updates[] = "playback_state = 'paused'";
            $updates[] = 'playback_position = :pos';
            $params[':pos'] = (float)($b['position'] ?? 0);
        })(),
        'seek' => (function() use (&$updates, &$params, $b) {
            $updates[] = 'playback_position = :pos';
            $params[':pos'] = (float)($b['position'] ?? 0);
        })(),
        'next' => (function() use (&$updates, &$params, $b, $session) {
            $audio_id  = $b['audio_id'] ?? null;
            $updates[] = 'current_audio_id = :audio_id';
            $updates[] = 'playback_position = 0';
            $updates[] = "playback_state = 'playing'";
            $params[':audio_id'] = $audio_id;
        })(),
        'live_voice' => (function() use (&$updates, &$params, $b) {
            $url = $b['room_url'] ?? null;
            $updates[] = 'live_voice_url = :lv_url';
            $params[':lv_url'] = $url;
        })(),
        'webrtc_offer' => (function() use (&$updates, &$params, $b) {
            $updates[] = 'webrtc_offer = :offer';
            $updates[] = 'webrtc_answer = NULL';
            $params[':offer'] = $b['offer'] ?? null;
        })(),
        'webrtc_answer' => (function() use (&$updates, &$params, $b) {
            $updates[] = 'webrtc_answer = :answer';
            $params[':answer'] = $b['answer'] ?? null;
        })(),
        'webrtc_stop' => (function() use (&$updates) {
            $updates[] = 'webrtc_offer = NULL';
            $updates[] = 'webrtc_answer = NULL';
        })(),
        'end' => (function() use (&$updates) {
            $updates[] = "status = 'ended'";
            $updates[] = "playback_state = 'idle'";
            $updates[] = 'ended_at = NOW()';
        })(),
    };

    db()->prepare("UPDATE tour_sessions SET " . implode(', ', $updates) . " WHERE pin=:pin")
        ->execute($params);

    json_ok(['cmd' => $cmd], 'OK');
}

// ── 5. Ping (khách báo còn online) ───────────────────────────

function tg_ping(string $pin): void {
    $session = tg_get_session($pin);
    $uid     = get_token_uid();
    $anon_id = $_SERVER['HTTP_X_ANONYMOUS_ID'] ?? '';

    $where  = $uid ? 'user_id=:id' : 'anonymous_id=:id';
    $id_val = $uid ?? $anon_id;

    db()->prepare("
        UPDATE tour_session_members SET last_ping=NOW()
        WHERE session_id=:sid AND $where AND left_at IS NULL
    ")->execute([':sid' => $session['id'], ':id' => $id_val]);

    tg_cleanup_stale($pin);
    tg_update_member_count($session['id']);

    json_ok(['member_count' => (int)$session['member_count']]);
}

// ── 6. Khách rời phiên ────────────────────────────────────────

function tg_leave(string $pin): void {
    $s = db()->prepare("SELECT id FROM tour_sessions WHERE pin=:pin");
    $s->execute([':pin' => $pin]);
    $session = $s->fetch();
    if (!$session) { json_ok(null); return; }

    $uid     = get_token_uid();
    $anon_id = $_SERVER['HTTP_X_ANONYMOUS_ID'] ?? '';

    $where  = $uid ? 'user_id=:id' : 'anonymous_id=:id';
    $id_val = $uid ?? $anon_id;

    db()->prepare("
        UPDATE tour_session_members SET left_at=NOW()
        WHERE session_id=:sid AND $where AND left_at IS NULL
    ")->execute([':sid' => $session['id'], ':id' => $id_val]);

    tg_update_member_count($session['id']);
    json_ok(null, 'Đã rời phiên');
}

// ── 7. Danh sách khách (guide) ────────────────────────────────

function tg_members(string $pin): void {
    $session = tg_get_session($pin);
    tg_require_host($session);

    tg_cleanup_stale($pin);

    $s = db()->prepare("
        SELECT m.id, m.lang, m.joined_at, m.last_ping,
               u.display_name, u.avatar_url
        FROM tour_session_members m
        LEFT JOIN users u ON u.id = m.user_id
        WHERE m.session_id=:sid AND m.left_at IS NULL
        ORDER BY m.joined_at ASC
    ");
    $s->execute([':sid' => $session['id']]);
    $members = $s->fetchAll();

    // Ẩn thông tin cá nhân nếu chưa đăng nhập
    foreach ($members as &$m) {
        if (!$m['display_name']) {
            $m['display_name'] = 'Khách ẩn danh';
            $m['avatar_url']   = null;
        }
    }

    json_ok([
        'members'      => $members,
        'member_count' => count($members),
    ]);
}

function tour_audio_points(string $tour_id): void {
    try {
    $lang = str_param('lang');
    // Lấy site_id từ voice_tour
    $vt = db()->prepare("SELECT site_id FROM voice_tours WHERE id=:id LIMIT 1");
    $vt->execute([':id' => $tour_id]);
    $vtRow = $vt->fetch();
    if (!$vtRow) json_error('Tour không tồn tại', 404);
    $stmt = db()->prepare("
        SELECT ap.id, ap.site_id, ap.order_index, ap.title, ap.short_description,
               ap.latitude, ap.longitude, ap.audio_url,
               ap.duration_sec, ap.floor, ap.zone_name, ap.is_required_stop,
               ap.map_x, ap.map_y, ap.status
        FROM audio_points ap
        WHERE ap.site_id = :sid
          AND ap.status = 'active'
        ORDER BY ap.order_index ASC
    ");
    $stmt->execute([':sid' => $vtRow['site_id']]);
    $rows = $stmt->fetchAll();
    foreach ($rows as &$r) {
        $r['order_index']       = (int)$r['order_index'];
        $r['duration_sec']      = (int)$r['duration_sec'];
        $r['floor']             = (int)$r['floor'];
        $r['is_required_stop']  = (bool)$r['is_required_stop'];
        $r['latitude']          = (float)$r['latitude'];
        $r['longitude']         = (float)$r['longitude'];
        $r['map_x']             = $r['map_x'] ? (float)$r['map_x'] : null;
        $r['map_y']             = $r['map_y'] ? (float)$r['map_y'] : null;
    }
    json_ok($rows);
    } catch (\Throwable $e) { json_error('DB: ' . $e->getMessage(), 500); }
}
