<?php
/**
 * Heritage Voice — partners.php
 * Phase 2: Local Tourism POI
 *
 * GET    /api/partners                          Danh sách đối tác (admin)
 * POST   /api/partners                          Đăng ký đối tác mới (public)
 * GET    /api/partners/nearby                   Đối tác gần GPS / di tích (public)
 * GET    /api/partners/{uuid}                   Chi tiết đối tác (public)
 * PUT    /api/partners/{uuid}                   Cập nhật đối tác (admin / chủ)
 * PUT    /api/partners/{uuid}/status            Duyệt / từ chối (admin)
 * GET    /api/partners/{uuid}/vouchers          Voucher của đối tác (public)
 * POST   /api/partners/{uuid}/vouchers          Tạo voucher (admin / chủ)
 * POST   /api/partners/{uuid}/click             Ghi nhận click referral (public)
 * POST   /api/partners/{uuid}/review            Gửi đánh giá (member+)
 * GET    /api/partners/{uuid}/reviews           Danh sách đánh giá (public)
 * GET    /api/partners/reports                  Báo cáo hoa hồng (admin)
 */


// ── MY PROFILE (Đối tác xem hồ sơ của mình) ──────────────────
function partner_my_profile() {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);

    $stmt = db()->prepare("
        SELECT p.*,
            ROUND(AVG(r.rating), 1) AS avg_rating,
            COUNT(r.id)             AS review_count,
            (SELECT MIN(mi.price) FROM partner_menu_items mi WHERE mi.partner_id=p.id AND mi.is_active=1) AS menu_price_min,
            (SELECT MAX(mi.price) FROM partner_menu_items mi WHERE mi.partner_id=p.id AND mi.is_active=1) AS menu_price_max,
            (SELECT MIN(rm.price_night) FROM partner_rooms rm WHERE rm.partner_id=p.id AND rm.is_active=1) AS room_price_min
        FROM hv_partners p
        LEFT JOIN hv_partner_reviews r
            ON r.partner_id = p.id AND r.status = 'approved'
        WHERE p.user_id = :uid
        GROUP BY p.id
        LIMIT 1
    ");
    $stmt->execute([':uid' => $uid]);
    $row = $stmt->fetch();

    if (!$row) { json_ok(null); return; } // Chưa có hồ sơ

    // Linked sites
    $ls = db()->prepare("
        SELECT l.heritage_site_id, l.distance_m, l.is_primary,
               hs.name AS site_name, hs.cover_image
        FROM hv_partner_heritage_links l
        JOIN heritage_sites hs ON hs.id = l.heritage_site_id
        WHERE l.partner_id = :pid
        ORDER BY l.is_primary DESC, l.distance_m ASC
    ");
    $ls->execute([':pid' => $row['id']]);
    $row['linked_sites'] = $ls->fetchAll();

    $row['lat']         = (float)$row['lat'];
    $row['lng']         = (float)$row['lng'];
    $row['price_range'] = (int)$row['price_range'];
    $row['is_featured'] = (bool)$row['is_featured'];
    $row['view_count']  = (int)$row['view_count'];
    $row['click_count'] = (int)$row['click_count'];
    $row['avg_rating']  = $row['avg_rating'] ? (float)$row['avg_rating'] : null;
    $row['review_count']= (int)$row['review_count'];
    $row['images']      = $row['images']     ? json_decode($row['images'],     true) : [];
    $row['amenities']   = $row['amenities']  ? json_decode($row['amenities'],  true) : [];
    $row['open_hours']  = $row['open_hours'] ? json_decode($row['open_hours'], true) : null;

    json_ok($row);
}


// ── PARTNER REVIEWS ADMIN ─────────────────────────────────────
function partner_reviews_pending() {
    require_admin();
    $stmt = db()->prepare("
        SELECT r.id, r.rating, r.comment, r.status, r.created_at,
               p.name AS partner_name,
               u.display_name AS user_name
        FROM hv_partner_reviews r
        JOIN hv_partners p ON p.id = r.partner_id
        LEFT JOIN users u ON u.id = r.user_id
        WHERE r.status = 'pending'
        ORDER BY r.created_at DESC
        LIMIT 50
    ");
    $stmt->execute();
    $items = $stmt->fetchAll();
    json_ok(['items' => $items]);
}

function partner_review_update(string $id) {
    require_admin();
    $b      = body();
    $status = $b['status'] ?? '';
    if (!in_array($status, ['approved','rejected'])) json_error('Trạng thái không hợp lệ');
    db()->prepare("UPDATE hv_partner_reviews SET status = :s WHERE id = :id")
        ->execute([':s' => $status, ':id' => $id]);
    audit_log('review_' . $status, 'partner_review', $id);
    json_ok(null, 'Đã cập nhật');
}


// ── REVIEW LIKE ───────────────────────────────────────────────
function partner_review_like(string $uuid, string $rid): void {
    db()->prepare("UPDATE hv_partner_reviews SET likes = likes + 1 WHERE id = :id AND partner_id = (SELECT id FROM hv_partners WHERE uuid = :uuid)")
        ->execute([':id' => $rid, ':uuid' => $uuid]);
    $row = db()->prepare("SELECT likes FROM hv_partner_reviews WHERE id = :id")->execute([':id' => $rid]) ? db()->query("SELECT likes FROM hv_partner_reviews WHERE id = $rid")->fetch() : null;
    json_ok(['likes' => (int)($row['likes'] ?? 0)]);
}

// ── REVIEW REPLY ──────────────────────────────────────────────
function partner_review_reply(string $uuid, string $rid): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);

    // Chỉ chủ cơ sở mới được reply
    $role = current_user_role();
    if (!in_array($role, ['admin','superadmin','editor'])) {
        $ck = db()->prepare("SELECT id FROM hv_partners WHERE uuid = :uuid AND user_id = :uid");
        $ck->execute([':uuid' => $uuid, ':uid' => $uid]);
        if (!$ck->fetch()) json_error('Forbidden', 403);
    }

    $b       = body();
    $content = trim($b['content'] ?? '');
    if (!$content) json_error('Nội dung không được trống');

    $partner = db()->prepare("SELECT id FROM hv_partners WHERE uuid = :uuid");
    $partner->execute([':uuid' => $uuid]);
    $p = $partner->fetch();
    if (!$p) json_error('Không tìm thấy đối tác', 404);

    // Xóa reply cũ nếu có (1 reply/review)
    db()->prepare("DELETE FROM hv_partner_review_replies WHERE review_id = :rid AND partner_id = :pid")
        ->execute([':rid' => $rid, ':pid' => $p['id']]);

    db()->prepare("INSERT INTO hv_partner_review_replies (review_id, partner_id, content) VALUES (:rid, :pid, :content)")
        ->execute([':rid' => $rid, ':pid' => $p['id'], ':content' => $content]);

    json_ok(['content' => $content], 'Đã phản hồi');
}

// ── GET REVIEWS WITH REPLIES ───────────────────────────────────
function partner_reviews_list(string $uuid): void {
    $p = _partner_by_uuid($uuid);
    $page  = max(1, (int)($_GET['page']  ?? 1));
    $limit = min(20, (int)($_GET['limit'] ?? 5));
    $offset = ($page - 1) * $limit;

    $total = db()->prepare("SELECT COUNT(*) FROM hv_partner_reviews WHERE partner_id = :pid AND status = 'approved'");
    $total->execute([':pid' => $p['id']]);
    $totalCount = (int)$total->fetchColumn();

    $stmt = db()->prepare("
        SELECT r.id, r.rating, r.comment, r.likes, r.created_at,
               u.display_name AS user_name, u.avatar_url,
               rp.content AS reply_content, rp.created_at AS reply_at
        FROM hv_partner_reviews r
        LEFT JOIN users u ON u.id = r.user_id
        LEFT JOIN hv_partner_review_replies rp ON rp.review_id = r.id
        WHERE r.partner_id = :pid AND r.status = 'approved'
        ORDER BY r.created_at DESC
        LIMIT :limit OFFSET :offset
    ");
    $stmt->bindValue(':pid',    $p['id'], PDO::PARAM_INT);
    $stmt->bindValue(':limit',  $limit,   PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset,  PDO::PARAM_INT);
    $stmt->execute();
    $items = $stmt->fetchAll();

    foreach ($items as &$r) {
        $r['likes']  = (int)$r['likes'];
        $r['rating'] = (int)$r['rating'];
    }

    json_ok([
        'items' => $items,
        'pagination' => [
            'total'  => $totalCount,
            'pages'  => ceil($totalCount / $limit),
            'page'   => $page,
            'limit'  => $limit,
        ]
    ]);
}


// ── VALIDATE VOUCHER ──────────────────────────────────────────

function partner_voucher_update(string $uuid, string $vid): void {
    $u = require_auth();
    $ps = db()->prepare("SELECT id, user_id FROM hv_partners WHERE uuid=:u");
    $ps->execute([':u'=>$uuid]);
    $p = $ps->fetch();
    if (!$p) json_error('Không tìm thấy', 404);
    if ($p['user_id'] != $u['id'] && !in_array($u['role'],['admin','superadmin'])) json_error('Không có quyền', 403);

    $b = body();
    $fields=[]; $params=[':id'=>$vid, ':pid'=>$p['id']];
    if (isset($b['code']))         { $fields[]='code=:code';           $params[':code']=$b['code']; }
    if (isset($b['discount_pct'])) { $fields[]='discount_pct=:pct';   $params[':pct']=(int)$b['discount_pct']; }
    if (isset($b['discount_amt'])) { $fields[]='discount_amt=:amt';   $params[':amt']=(int)$b['discount_amt']; }
    if (isset($b['max_uses']))     { $fields[]='max_uses=:uses';       $params[':uses']=(int)$b['max_uses']; }
    if (isset($b['expires_at']))   { $fields[]='expires_at=:exp';     $params[':exp']=$b['expires_at']?:null; }
    if (isset($b['is_active']))    { $fields[]='is_active=:active';   $params[':active']=(int)$b['is_active']; }
    if (!$fields) json_error('Không có dữ liệu cập nhật');

    db()->prepare("UPDATE hv_partner_vouchers SET ".implode(',', $fields)." WHERE id=:id AND partner_id=:pid")
        ->execute($params);
    json_ok(null, 'Đã cập nhật voucher');
}


function partner_voucher_toggle(string $uuid, string $vcode): void {
    require_auth();
    $ps = db()->prepare("SELECT id FROM hv_partners WHERE uuid=:u");
    $ps->execute([':u'=>$uuid]);
    $p = $ps->fetch();
    if (!$p) json_error('Không tìm thấy', 404);
    $b = body();
    $status = in_array($b['status']??'', ['active','inactive']) ? $b['status'] : 'inactive';
    db()->prepare("UPDATE hv_partner_vouchers SET status=:s WHERE code=:c AND partner_id=:pid")
        ->execute([':s'=>$status, ':c'=>$vcode, ':pid'=>$p['id']]);
    json_ok(null, 'Đã cập nhật');
}

function partner_voucher_validate(string $uuid): void {
    rate_limit('voucher_validate', 10, 60); // 10 lần/phút
    $p    = _partner_by_uuid($uuid);
    $b    = body();
    $code = strtoupper(trim($b['code'] ?? ''));
    if (!$code) json_error('Thiếu mã voucher');

    $stmt = db()->prepare("
        SELECT * FROM hv_partner_vouchers
        WHERE partner_id = :pid
          AND code = :code
          AND status = 'active'
          AND valid_from <= CURDATE()
          AND valid_until >= CURDATE()
          AND (max_uses = 0 OR used_count < max_uses)
        LIMIT 1
    ");
    $stmt->execute([':pid' => $p['id'], ':code' => $code]);
    $v = $stmt->fetch();

    if (!$v) json_error('Mã không hợp lệ hoặc đã hết hạn', 404);

    json_ok([
        'code'           => $v['code'],
        'title'          => $v['title'],
        'discount_type'  => $v['discount_type'],
        'discount_value' => (float)$v['discount_value'],
        'min_order'      => (float)$v['min_order'],
        'valid_until'    => $v['valid_until'],
    ]);
}

// ── HELPERS NỘI BỘ ────────────────────────────────────────────

function _partner_by_uuid(string $uuid): array {
    $s = db()->prepare("SELECT * FROM hv_partners WHERE uuid = :uuid");
    $s->execute([':uuid' => $uuid]);
    $r = $s->fetch();
    if (!$r) json_error('Không tìm thấy đối tác', 404);
    return $r;
}

function _make_uuid(): string {
    $s = db()->query("SELECT UUID() AS u");
    return $s->fetchColumn();
}

function _make_ref_code(): string {
    $s = db()->query("SELECT UPPER(SUBSTRING(MD5(RAND()), 1, 12)) AS c");
    return $s->fetchColumn();
}

function _cast_partner(array $r): array {
    $r['lat']         = (float)$r['lat'];
    $r['lng']         = (float)$r['lng'];
    $r['price_range'] = (int)$r['price_range'];
    $r['is_featured'] = (bool)$r['is_featured'];
    $r['view_count']  = (int)$r['view_count'];
    $r['click_count'] = (int)$r['click_count'];
    $r['images']      = $r['images']    ? json_decode($r['images'],    true) : [];
    $r['amenities']   = $r['amenities'] ? json_decode($r['amenities'], true) : [];
    $r['open_hours']  = $r['open_hours']? json_decode($r['open_hours'],true) : null;
    // Check xem user hiện tại có phải chủ cơ sở không
    $r['is_owner'] = false;
    try {
        $cu = hv_get_user();
        if ($cu && !empty($r['owner_email']) && strtolower($cu['email']) === strtolower($r['owner_email'])) {
            $r['is_owner'] = true;
        }
    } catch (Throwable $e) {}

    // Ẩn thông tin nhạy cảm với public
    unset($r['id'], $r['commission_rate'], $r['owner_email'],
          $r['owner_phone'], $r['tax_code'], $r['verified_by'],
          $r['reject_reason']);
    return $r;
}

// ── 1. DANH SÁCH ĐỐI TÁC (Admin) ─────────────────────────────

function partners_list(): void {
    require_admin();

    $page   = int_param('page', 1, 1);
    $limit  = int_param('limit', 20, 1, 100);
    $offset = ($page - 1) * $limit;
    $status = str_param('status', 'all');
    $type   = str_param('type');
    $search = str_param('search');

    $where  = [];
    $params = [];

    if ($status !== 'all') {
        $where[]           = 'status = :status';
        $params[':status'] = $status;
    }
    if ($type) {
        $where[]         = 'type = :type';
        $params[':type'] = $type;
    }
    if ($search) {
        $where[]       = '(name LIKE :s OR address LIKE :s2 OR owner_name LIKE :s3)';
        $params[':s']  = "%$search%";
        $params[':s2'] = "%$search%";
        $params[':s3'] = "%$search%";
    }
    // Lọc theo user_id (cho tab Đối tác trong Profile)
    $user_id_filter = int_param('user_id');
    if ($user_id_filter) {
        $where[]            = 'user_id = :user_id';
        $params[':user_id'] = $user_id_filter;
    }

    $w = $where ? 'WHERE ' . implode(' AND ', $where) : '';

    $cs = db()->prepare("SELECT COUNT(*) FROM hv_partners $w");
    $cs->execute($params);
    $total = (int)$cs->fetchColumn();

    $stmt = db()->prepare("
        SELECT id, uuid, name, type, phone, email, address,
               lat, lng, status, is_featured, commission_rate,
               view_count, click_count, created_at, verified_at
        FROM hv_partners $w
        ORDER BY
          FIELD(status,'pending','active','suspended','rejected'),
          is_featured DESC,
          created_at DESC
        LIMIT :limit OFFSET :offset
    ");
    foreach ($params as $k => $v) $stmt->bindValue($k, $v);
    $stmt->bindValue(':limit',  $limit,  PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();

    $rows = $stmt->fetchAll();
    foreach ($rows as &$r) {
        $r['lat']         = (float)$r['lat'];
        $r['lng']         = (float)$r['lng'];
        $r['is_featured'] = (bool)$r['is_featured'];
        $r['view_count']  = (int)$r['view_count'];
        $r['click_count'] = (int)$r['click_count'];
    }

    json_ok(['items' => $rows, 'pagination' => paginate($total, $page, $limit)]);
}

// ── 2. ĐĂNG KÝ ĐỐI TÁC MỚI (Public) ─────────────────────────

function partner_register(): void {
    $b = body();

    // Validate bắt buộc
    $required = ['name', 'type', 'address', 'lat', 'lng',
                 'owner_name', 'owner_phone', 'owner_email'];
    foreach ($required as $f) {
        if (empty($b[$f])) json_error("Thiếu trường bắt buộc: $f");
    }

    $lat  = (float)$b['lat'];
    $lng  = (float)$b['lng'];
    $name = trim($b['name']);
    $type = $b['type'];

    $allowed_types = ['restaurant','hotel','guesthouse','cafe','transport','shop','other'];
    if (!in_array($type, $allowed_types)) json_error('Loại hình không hợp lệ');
    if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) json_error('Tọa độ không hợp lệ');
    if (!filter_var($b['owner_email'], FILTER_VALIDATE_EMAIL)) json_error('Email không hợp lệ');

    // Validate khoảng cách với di tích gắn kèm (nếu có)
    $site_links = $b['heritage_sites'] ?? []; // [{ site_id, is_primary }]
    $links_to_insert = [];

    if (!empty($site_links)) {
        foreach ($site_links as $link) {
            $site_id = trim($link['site_id'] ?? '');
            if (!$site_id) continue;

            // Lấy tọa độ di tích
            $ss = db()->prepare("SELECT latitude, longitude, name FROM heritage_sites WHERE id = :id AND status = 'active'");
            $ss->execute([':id' => $site_id]);
            $site = $ss->fetch();
            if (!$site) json_error("Di tích '$site_id' không tồn tại hoặc chưa active");

            // Tính khoảng cách bằng haversine() PHP (helpers.php)
            $dist_m = (int)haversine($lat, $lng, (float)$site['latitude'], (float)$site['longitude']);

            if ($dist_m > 15000) {
                json_error("Đối tác cách di tích '{$site['name']}' {$dist_m}m — vượt quá giới hạn 15km");
            }

            $links_to_insert[] = [
                'site_id'    => $site_id,
                'distance_m' => $dist_m,
                'is_primary' => (int)!empty($link['is_primary']),
            ];
        }
    }

    // Lấy user_id từ token nếu đã đăng nhập
    $current_uid = get_token_uid();

    // Insert partner
    $uuid = _make_uuid();
    db()->prepare("
        INSERT INTO hv_partners (
            uuid, user_id, name, type, phone, email, website,
            address, lat, lng, google_place_id,
            description, images, amenities, open_hours, price_range,
            owner_name, owner_phone, owner_email, tax_code,
            commission_rate, status
        ) VALUES (
            :uuid, :uid, :name, :type, :phone, :email, :website,
            :address, :lat, :lng, :gplace,
            :desc, :images, :amenities, :hours, :price,
            :oname, :ophone, :oemail, :tax,
            5.00, 'pending'
        )
    ")->execute([
        ':uuid'     => $uuid,
        ':uid'      => $current_uid,
        ':name'     => $name,
        ':type'     => $type,
        ':phone'    => trim($b['phone']    ?? ''),
        ':email'    => trim($b['email']    ?? ''),
        ':website'  => trim($b['website']  ?? ''),
        ':address'  => trim($b['address']),
        ':lat'      => $lat,
        ':lng'      => $lng,
        ':gplace'   => trim($b['google_place_id'] ?? ''),
        ':desc'     => trim($b['description'] ?? ''),
        ':images'   => !empty($b['images'])    ? json_encode($b['images'])    : null,
        ':amenities'=> !empty($b['amenities']) ? json_encode($b['amenities']) : null,
        ':hours'    => !empty($b['open_hours'])? json_encode($b['open_hours']): null,
        ':price'    => (int)($b['price_range'] ?? 2),
        ':oname'    => trim($b['owner_name']),
        ':ophone'   => trim($b['owner_phone']),
        ':oemail'   => trim($b['owner_email']),
        ':tax'      => trim($b['tax_code'] ?? ''),
    ]);

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

    // Insert links với di tích
    foreach ($links_to_insert as $lk) {
        db()->prepare("
            INSERT IGNORE INTO hv_partner_heritage_links
                (partner_id, heritage_site_id, distance_m, is_primary, display_order)
            VALUES (:pid, :sid, :dist, :primary, 10)
        ")->execute([
            ':pid'     => $partner_id,
            ':sid'     => $lk['site_id'],
            ':dist'    => $lk['distance_m'],
            ':primary' => $lk['is_primary'],
        ]);
    }

    json_ok(['uuid' => $uuid], 'Đăng ký thành công! Hồ sơ đang chờ xét duyệt.');
}

// ── 3. ĐỐI TÁC GẦN ĐÂY (Public) ─────────────────────────────

function partners_nearby(): void {
    $lat      = float_param('lat');
    $lng      = float_param('lng');
    $site_id  = str_param('site_id');   // hoặc lọc theo di tích
    $type     = str_param('type');      // lọc theo loại
    $radius   = int_param('radius', 2000, 100, 15000); // mét, mặc định 2km
    $limit    = int_param('limit', 20, 1, 50);

    // Phải có GPS hoặc site_id
    if ($lat === null && !$site_id) {
        json_error('Cần truyền lat/lng hoặc site_id');
    }

    // Nếu chỉ có site_id → lấy tọa độ di tích làm tâm
    if ($lat === null && $site_id) {
        $ss = db()->prepare("SELECT latitude, longitude FROM heritage_sites WHERE id = :id");
        $ss->execute([':id' => $site_id]);
        $site = $ss->fetch();
        if (!$site) json_error('Di tích không tồn tại', 404);
        $lat = (float)$site['latitude'];
        $lng = (float)$site['longitude'];
    }

    // Bounding box để MySQL dùng index lat/lng trước
    $dlat = $radius / 111320;
    $dlng = $radius / (111320 * cos(deg2rad($lat)));

    $type_cond   = $type ? 'AND p.type = :type' : '';
    $site_cond   = $site_id ? 'AND l.heritage_site_id = :site_id' : '';

    $stmt = db()->prepare("
        SELECT
            p.uuid, p.name, p.type,
            p.phone, p.address, p.lat, p.lng,
            p.images, p.open_hours, p.price_range,
            p.is_featured, p.view_count, p.click_count,
            COALESCE(l.distance_m,
                ROUND(6371000 * ACOS(LEAST(1.0,
                    COS(RADIANS(:lat1)) * COS(RADIANS(p.lat))
                    * COS(RADIANS(p.lng) - RADIANS(:lng1))
                    + SIN(RADIANS(:lat2)) * SIN(RADIANS(p.lat))
                )))
            ) AS distance_m,
            ROUND(AVG(r.rating), 1) AS avg_rating,
            COUNT(r.id)             AS review_count,
            (SELECT MIN(mi.price) FROM partner_menu_items mi WHERE mi.partner_id=p.id AND mi.is_active=1) AS menu_price_min,
            (SELECT MAX(mi.price) FROM partner_menu_items mi WHERE mi.partner_id=p.id AND mi.is_active=1) AS menu_price_max,
            (SELECT MIN(rm.price_night) FROM partner_rooms rm WHERE rm.partner_id=p.id AND rm.is_active=1) AS room_price_min
        FROM hv_partners p
        LEFT JOIN hv_partner_heritage_links l
            ON l.partner_id = p.id $site_cond
        LEFT JOIN hv_partner_reviews r
            ON r.partner_id = p.id AND r.status = 'approved'
        WHERE p.status = 'active'
          AND p.lat BETWEEN :lat_min AND :lat_max
          AND p.lng BETWEEN :lng_min AND :lng_max
          $type_cond
        GROUP BY
            p.id, p.uuid, p.name, p.type, p.phone,
            p.address, p.lat, p.lng, p.images, p.open_hours,
            p.price_range, p.is_featured, p.view_count,
            p.click_count
        ORDER BY p.is_featured DESC, distance_m ASC
        LIMIT 100
    ");

    $params = [
        ':lat1'    => $lat, ':lng1'    => $lng,
        ':lat2'    => $lat,
        ':lat_min' => $lat - $dlat, ':lat_max' => $lat + $dlat,
        ':lng_min' => $lng - $dlng, ':lng_max' => $lng + $dlng,
    ];
    if ($site_id) $params[':site_id'] = $site_id;
    if ($type)    $params[':type']    = $type;

    foreach ($params as $k => $v) $stmt->bindValue($k, $v);
    $stmt->execute();
    // Filter radius PHP (MySQL 5.7 không hỗ trợ HAVING với alias)
    $all  = $stmt->fetchAll();
    $rows = array_slice(array_values(array_filter($all, function($r) use ($radius) {
        return (int)$r['distance_m'] <= (int)$radius;
    })), 0, $limit);
    foreach ($rows as &$r) {
        $r['lat']          = (float)$r['lat'];
        $r['lng']          = (float)$r['lng'];
        $r['distance_m']   = (int)$r['distance_m'];
        $r['price_range']  = (int)$r['price_range'];
        $r['is_featured']  = (bool)$r['is_featured'];
        $r['view_count']   = (int)$r['view_count'];
        $r['click_count']  = (int)$r['click_count'];
        $r['avg_rating']   = $r['avg_rating'] ? (float)$r['avg_rating'] : null;
        $r['review_count']   = (int)$r['review_count'];
        $r['menu_price_min'] = $r['menu_price_min'] ? (int)$r['menu_price_min'] : null;
        $r['menu_price_max'] = $r['menu_price_max'] ? (int)$r['menu_price_max'] : null;
        $r['room_price_min'] = $r['room_price_min'] ? (int)$r['room_price_min'] : null;
        $r['images']       = $r['images']    ? json_decode($r['images'],    true) : [];
        $r['open_hours']   = $r['open_hours']? json_decode($r['open_hours'],true) : null;
    }

    json_ok($rows);
}

// ── 4. CHI TIẾT ĐỐI TÁC (Public) ─────────────────────────────

function partner_detail(string $uuid): void {
    $stmt = db()->prepare("
        SELECT p.*,
               ROUND(AVG(r.rating), 1) AS avg_rating,
               COUNT(r.id)             AS review_count
        FROM hv_partners p
        LEFT JOIN hv_partner_reviews r
            ON r.partner_id = p.id AND r.status = 'approved'
        WHERE p.uuid = :uuid AND p.status = 'active'
        GROUP BY p.id
    ");
    $stmt->execute([':uuid' => $uuid]);
    $r = $stmt->fetch();
    if (!$r) json_error('Không tìm thấy đối tác', 404);

    // Tăng view count
    db()->prepare("UPDATE hv_partners SET view_count = view_count + 1 WHERE uuid = :uuid")
        ->execute([':uuid' => $uuid]);

    // Lấy di tích gắn kèm
    $ls = db()->prepare("
        SELECT l.heritage_site_id, l.distance_m, l.is_primary,
               hs.name AS site_name, hs.cover_image
        FROM hv_partner_heritage_links l
        JOIN heritage_sites hs ON hs.id = l.heritage_site_id
        WHERE l.partner_id = :pid
        ORDER BY l.is_primary DESC, l.distance_m ASC
    ");
    $ls->execute([':pid' => $r['id']]);
    $r['linked_sites'] = $ls->fetchAll();

    // Lấy voucher đang active
    $vs = db()->prepare("
        SELECT code, title, discount_type, discount_value,
               min_order, valid_until
        FROM hv_partner_vouchers
        WHERE partner_id = :pid AND status = 'active'
          AND valid_from <= CURDATE() AND valid_until >= CURDATE()
          AND (max_uses IS NULL OR used_count < max_uses)
        ORDER BY discount_value DESC
    ");
    $vs->execute([':pid' => $r['id']]);
    $r['vouchers'] = $vs->fetchAll();

    $r['avg_rating']   = $r['avg_rating'] ? (float)$r['avg_rating'] : null;
    $r['review_count'] = (int)$r['review_count'];

    json_ok(_cast_partner($r));
}

// ── 5. CẬP NHẬT ĐỐI TÁC (Admin) ─────────────────────────────

function partner_update(string $uuid): void {
    // Admin hoặc chủ cơ sở mới được sửa
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $role = current_user_role();
    if (!in_array($role, ['admin','superadmin','editor'])) {
        $ck = db()->prepare("SELECT id FROM hv_partners WHERE uuid = :uuid AND user_id = :uid");
        $ck->execute([':uuid' => $uuid, ':uid' => $uid]);
        if (!$ck->fetch()) json_error('Forbidden', 403);
    }
    $p = _partner_by_uuid($uuid);
    $b = body();

    $fields = ['name','type','phone','email','website','address',
               'description','price_range','is_featured','featured_until',
               'commission_rate','open_hours','amenities',
               'bank_name','bank_account','bank_holder'];
    $set    = [];
    $params = [':uuid' => $uuid];

    // lat/lng — validate lại khoảng cách nếu thay đổi
    if (isset($b['lat']) && isset($b['lng'])) {
        $nlat = (float)$b['lat'];
        $nlng = (float)$b['lng'];
        if ($nlat < -90 || $nlat > 90 || $nlng < -180 || $nlng > 180) {
            json_error('Tọa độ không hợp lệ');
        }
        $set[] = 'lat = :lat';
        $set[] = 'lng = :lng';
        $params[':lat'] = $nlat;
        $params[':lng'] = $nlng;
    }

    foreach ($fields as $f) {
        if (!array_key_exists($f, $b)) continue;
        if (in_array($f, ['open_hours','amenities'])) {
            $set[]       = "`$f` = :$f";
            $params[":$f"] = is_array($b[$f]) ? json_encode($b[$f]) : $b[$f];
        } else {
            $set[]       = "`$f` = :$f";
            $params[":$f"] = $b[$f];
        }
    }

    if (!empty($b['images'])) {
        $set[]           = '`images` = :images';
        $params[':images'] = json_encode($b['images']);
    }

    if (!$set) json_error('Không có trường nào để cập nhật');
    $set[] = 'updated_at = NOW()';

    db()->prepare("UPDATE hv_partners SET " . implode(', ', $set) . " WHERE uuid = :uuid")
        ->execute($params);

    json_ok(['uuid' => $uuid], 'Cập nhật thành công');
}

// ── 6. DUYỆT / TỪ CHỐI (Admin) ───────────────────────────────

function partner_set_status(string $uuid): void {
    require_admin();
    $b      = body();
    $status = $b['status'] ?? '';
    $allowed = ['active', 'suspended', 'rejected', 'pending'];

    if (!in_array($status, $allowed)) json_error('Trạng thái không hợp lệ');

    $params = [':status' => $status, ':uuid' => $uuid];
    $extra  = '';

    if ($status === 'active') {
        $extra = ', verified_at = NOW(), verified_by = :admin_id';
        $params[':admin_id'] = get_token_uid();


    }
    if ($status === 'rejected') {
        $params[':reason'] = trim($b['reason'] ?? 'Không đạt yêu cầu');
        $extra = ', reject_reason = :reason';
    }

    db()->prepare("UPDATE hv_partners SET status = :status $extra WHERE uuid = :uuid")
        ->execute($params);

    $msg = match($status) {
        'active'    => 'Đã duyệt đối tác',
        'rejected'  => 'Đã từ chối đối tác',
        'suspended' => 'Đã tạm khóa đối tác',
        default     => 'Đã cập nhật trạng thái',
    };
    audit_log('partner_status_' . $status, 'partner', $uuid, 'Status changed to: ' . $status);

    // Gửi email thông báo cho đối tác
    _send_partner_status_email($uuid, $status, $b['reason'] ?? '');

    json_ok(['uuid' => $uuid], $msg);
}

// ── GỬI EMAIL KHI DUYỆT / TỪ CHỐI ───────────────────────────
function _send_partner_status_email(string $uuid, string $status, string $reason = ''): void {
    if (!in_array($status, ['active', 'rejected', 'suspended'])) return;

    $ps = db()->prepare("SELECT name, owner_email, owner_name FROM hv_partners WHERE uuid = :uuid");
    $ps->execute([':uuid' => $uuid]);
    $partner = $ps->fetch();
    if (!$partner || empty($partner['owner_email'])) return;

    try {
        $mailerPath = dirname(__DIR__) . '/PHPMailer/src/';
        require_once $mailerPath . 'Exception.php';
        require_once $mailerPath . 'PHPMailer.php';
        require_once $mailerPath . 'SMTP.php';

        $mail = new PHPMailer\PHPMailer\PHPMailer(true);
        $mail->isSMTP();
        $mail->Host       = MAIL_HOST;
        $mail->SMTPAuth   = true;
        $mail->Username   = MAIL_USER;
        $mail->Password   = MAIL_PASS;
        $mail->SMTPSecure = 'tls';
        $mail->Port       = MAIL_PORT;
        $mail->CharSet    = 'UTF-8';
        $mail->setFrom(MAIL_FROM, MAIL_FROM_NAME);
        $mail->addAddress($partner['owner_email'], $partner['owner_name'] ?? $partner['name']);

        $appUrl = 'https://tads.ctv.hanoi.vn';

        if ($status === 'active') {
            $mail->Subject = "[Heritage Voice] 🎉 Hồ sơ đối tác đã được duyệt — {$partner['name']}";
            $mail->isHTML(true);
            $mail->Body = "
<html><body style='font-family:Arial,sans-serif;max-width:600px;margin:0 auto;background:#f5f2ee;padding:20px;'>
  <div style='background:#8B3E18;padding:24px;border-radius:12px 12px 0 0;text-align:center;'>
    <h2 style='color:#fff;margin:0;'>🏛 Heritage Voice</h2>
  </div>
  <div style='background:#fff;border:1px solid #e0d8d0;border-top:none;padding:24px;border-radius:0 0 12px 12px;'>
    <div style='text-align:center;margin-bottom:20px;'>
      <div style='font-size:48px;'>🎉</div>
      <h2 style='color:#2C1A0E;margin:8px 0;'>Chúc mừng! Hồ sơ đã được duyệt</h2>
      <p style='color:#666;'>Cơ sở <strong>{$partner['name']}</strong> đã được xét duyệt và hoạt động trên Heritage Voice.</p>
    </div>
    <div style='background:#E8F5E9;border-radius:10px;padding:16px;margin-bottom:20px;'>
      <p style='color:#2E7D32;font-weight:700;margin:0 0 8px;'>✅ Những gì bạn có thể làm ngay:</p>
      <ul style='color:#555;margin:0;padding-left:20px;line-height:1.8;'>
        <li>Cập nhật ảnh và thông tin cơ sở</li>
        <li>Thêm voucher ưu đãi cho du khách</li>
        <li>Theo dõi lượt xem và đặt chỗ</li>
      </ul>
    </div>
    <div style='text-align:center;'>
      <a href='{$appUrl}/profile.html' style='display:inline-block;background:#8B3E18;color:#fff;padding:12px 32px;border-radius:25px;text-decoration:none;font-weight:700;font-size:14px;'>
        Vào quản lý hồ sơ →
      </a>
    </div>
    <div style='margin-top:20px;padding-top:16px;border-top:1px solid #f0ece8;font-size:12px;color:#999;text-align:center;'>
      Heritage Voice · <a href='mailto:partner@heritage-voice.vn' style='color:#8B3E18;'>partner@heritage-voice.vn</a>
    </div>
  </div>
</body></html>";

        } elseif ($status === 'rejected') {
            $mail->Subject = "[Heritage Voice] Hồ sơ đối tác chưa được duyệt — {$partner['name']}";
            $mail->isHTML(true);
            $reasonText = $reason ?: 'Không đáp ứng tiêu chí hiện tại';
            $mail->Body = "
<html><body style='font-family:Arial,sans-serif;max-width:600px;margin:0 auto;background:#f5f2ee;padding:20px;'>
  <div style='background:#8B3E18;padding:24px;border-radius:12px 12px 0 0;text-align:center;'>
    <h2 style='color:#fff;margin:0;'>🏛 Heritage Voice</h2>
  </div>
  <div style='background:#fff;border:1px solid #e0d8d0;border-top:none;padding:24px;border-radius:0 0 12px 12px;'>
    <h2 style='color:#2C1A0E;'>Thông báo về hồ sơ đối tác</h2>
    <p style='color:#666;'>Xin chào <strong>{$partner['owner_name']}</strong>,</p>
    <p style='color:#666;'>Rất tiếc, hồ sơ của <strong>{$partner['name']}</strong> chưa được duyệt lần này.</p>
    <div style='background:#FFF3CD;border-radius:10px;padding:16px;margin:16px 0;'>
      <p style='color:#856404;font-weight:700;margin:0 0 6px;'>Lý do:</p>
      <p style='color:#856404;margin:0;'>" . htmlspecialchars($reasonText) . "</p>
    </div>
    <p style='color:#666;'>Bạn có thể cập nhật thông tin và đăng ký lại. Chúng tôi luôn chào đón các cơ sở chất lượng!</p>
    <div style='text-align:center;margin-top:20px;'>
      <a href='{$appUrl}/partner-register.html' style='display:inline-block;background:#8B3E18;color:#fff;padding:12px 32px;border-radius:25px;text-decoration:none;font-weight:700;'>
        Đăng ký lại →
      </a>
    </div>
  </div>
</body></html>";

        } elseif ($status === 'suspended') {
            $mail->Subject = "[Heritage Voice] Hồ sơ đối tác tạm khóa — {$partner['name']}";
            $mail->isHTML(true);
            $mail->Body = "
<html><body style='font-family:Arial,sans-serif;max-width:600px;margin:0 auto;background:#f5f2ee;padding:20px;'>
  <div style='background:#8B3E18;padding:24px;border-radius:12px 12px 0 0;text-align:center;'>
    <h2 style='color:#fff;margin:0;'>🏛 Heritage Voice</h2>
  </div>
  <div style='background:#fff;border:1px solid #e0d8d0;border-top:none;padding:24px;border-radius:0 0 12px 12px;'>
    <h2 style='color:#2C1A0E;'>Thông báo tạm khóa hồ sơ</h2>
    <p style='color:#666;'>Hồ sơ <strong>{$partner['name']}</strong> đã bị tạm khóa.</p>
    <p style='color:#666;'>Vui lòng liên hệ <a href='mailto:partner@heritage-voice.vn' style='color:#8B3E18;'>partner@heritage-voice.vn</a> để biết thêm chi tiết.</p>
  </div>
</body></html>";
        }

        $mail->send();
        error_log('[HV Partner Status Mail] Sent ' . $status . ' to ' . $partner['owner_email']);
    } catch (\Exception $e) {
        error_log('[HV Partner Status Mail ERROR] ' . $e->getMessage());
    }
}

// ── 7. VOUCHERS ───────────────────────────────────────────────

function partner_vouchers(string $uuid): void {
    $p    = _partner_by_uuid($uuid);
    $stmt = db()->prepare("
        SELECT code, title, description, discount_type, discount_value,
               min_order, max_uses, used_count, valid_from, valid_until, status
        FROM hv_partner_vouchers
        WHERE partner_id = :pid AND status = 'active'
          AND valid_from <= CURDATE() AND valid_until >= CURDATE()
        ORDER BY discount_value DESC
    ");
    $stmt->execute([':pid' => $p['id']]);
    json_ok($stmt->fetchAll());
}

function partner_voucher_create(string $uuid): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $role = current_user_role();
    if (!in_array($role, ['admin','superadmin','editor'])) {
        $ck = db()->prepare("SELECT id FROM hv_partners WHERE uuid = :uuid AND user_id = :uid");
        $ck->execute([':uuid' => $uuid, ':uid' => $uid]);
        if (!$ck->fetch()) json_error('Forbidden', 403);
    }
    $p = _partner_by_uuid($uuid);
    $b = body();

    $required = ['code','title','discount_type','discount_value','valid_from','valid_until'];
    foreach ($required as $f) {
        if (empty($b[$f])) json_error("Thiếu trường: $f");
    }
    if (!in_array($b['discount_type'], ['percent','fixed'])) json_error('discount_type không hợp lệ');
    if ($b['discount_type'] === 'percent' && $b['discount_value'] > 100) {
        json_error('Phần trăm giảm không thể vượt quá 100%');
    }

    // Kiểm tra code trùng
    $ck = db()->prepare("SELECT id FROM hv_partner_vouchers WHERE code = :code");
    $ck->execute([':code' => strtoupper(trim($b['code']))]);
    if ($ck->fetch()) json_error('Mã voucher đã tồn tại', 409);

    db()->prepare("
        INSERT INTO hv_partner_vouchers
            (partner_id, code, title, description, discount_type,
             discount_value, min_order, max_uses, valid_from, valid_until)
        VALUES
            (:pid, :code, :title, :desc, :dtype,
             :dval, :min, :max, :vfrom, :vuntil)
    ")->execute([
        ':pid'    => $p['id'],
        ':code'   => strtoupper(trim($b['code'])),
        ':title'  => trim($b['title']),
        ':desc'   => trim($b['description'] ?? ''),
        ':dtype'  => $b['discount_type'],
        ':dval'   => (float)$b['discount_value'],
        ':min'    => isset($b['min_order']) ? (float)$b['min_order'] : null,
        ':max'    => isset($b['max_uses'])  ? (int)$b['max_uses']    : null,
        ':vfrom'  => $b['valid_from'],
        ':vuntil' => $b['valid_until'],
    ]);

    json_ok(null, 'Tạo voucher thành công');
}

// ── 8. CLICK / REFERRAL TRACKING (Public) ─────────────────────

function partner_click(string $uuid): void {
    $p      = _partner_by_uuid($uuid);
    $b      = body();
    $uid    = get_token_uid();
    $anon   = $_SERVER['HTTP_X_ANONYMOUS_ID'] ?? null;
    $ref    = _make_ref_code();

    db()->prepare("
        INSERT INTO hv_bookings
            (ref_code, partner_id, user_id, anon_id, type, status,
             heritage_site_id, user_lat, user_lng, commission_rate)
        VALUES
            (:ref, :pid, :uid, :anon, 'referral', 'clicked',
             :site, :lat, :lng, :rate)
    ")->execute([
        ':ref'  => $ref,
        ':pid'  => $p['id'],
        ':uid'  => $uid,
        ':anon' => $anon,
        ':site' => $b['heritage_site_id'] ?? null,
        ':lat'  => isset($b['lat']) ? (float)$b['lat'] : null,
        ':lng'  => isset($b['lng']) ? (float)$b['lng'] : null,
        ':rate' => (float)$p['commission_rate'],
    ]);

    // Tăng click count
    db()->prepare("UPDATE hv_partners SET click_count = click_count + 1 WHERE id = :id")
        ->execute([':id' => $p['id']]);

    json_ok([
        'ref_code'  => $ref,
        'redirect'  => $p['website'] ? rtrim($p['website'], '/').'?ref='.$ref.'&utm_source=heritage_voice' : null,
    ]);
}

// ── 9. REVIEW (Member+) ───────────────────────────────────────

function partner_review_submit(string $uuid): void {
    rate_limit('review', 5, 60);
    $uid = get_token_uid();
    if (!$uid) json_error('Vui lòng đăng nhập để đánh giá', 401);
    $u = null;
    $p = _partner_by_uuid($uuid);
    $b = body();

    $rating = (int)($b['rating'] ?? 0);
    if ($rating < 1 || $rating > 5) json_error('Đánh giá phải từ 1 đến 5 sao');

    $anon = $_SERVER['HTTP_X_ANONYMOUS_ID'] ?? null;
    // Kiểm tra đã review chưa
    $ck = db()->prepare("SELECT id FROM hv_partner_reviews WHERE partner_id=:pid AND user_id=:uid");
    $ck->execute([':pid' => $p['id'], ':uid' => $uid]);
    if ($ck->fetch()) json_error('Bạn đã đánh giá cơ sở này rồi', 409);

    db()->prepare("
        INSERT INTO hv_partner_reviews
            (partner_id, user_id, anon_id, rating, comment, status)
        VALUES
            (:pid, :uid, :anon, :rating, :comment, 'pending')
    ")->execute([
        ':pid'     => $p['id'],
        ':uid'     => $u ? $u['id'] : null,
        ':anon'    => $anon,
        ':rating'  => $rating,
        ':comment' => trim($b['comment'] ?? ''),
    ]);

    json_ok(null, 'Cảm ơn bạn đã đánh giá! Đánh giá đang chờ duyệt.');
}

function partner_reviews(string $uuid): void {
    $p     = _partner_by_uuid($uuid);
    $page  = int_param('page', 1, 1);
    $limit = int_param('limit', 10, 1, 50);
    $offset= ($page - 1) * $limit;

    $cs = db()->prepare("SELECT COUNT(*) FROM hv_partner_reviews WHERE partner_id = :pid AND status = 'approved'");
    $cs->execute([':pid' => $p['id']]);
    $total = (int)$cs->fetchColumn();

    $stmt = db()->prepare("
        SELECT r.rating, r.comment, r.created_at,
               u.display_name AS user_name
        FROM hv_partner_reviews r
        LEFT JOIN users u ON u.id = r.user_id
        WHERE r.partner_id = :pid AND r.status = 'approved'
        ORDER BY r.created_at DESC
        LIMIT :limit OFFSET :offset
    ");
    $stmt->bindValue(':pid',    $p['id'], PDO::PARAM_INT);
    $stmt->bindValue(':limit',  $limit,   PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset,  PDO::PARAM_INT);
    $stmt->execute();

    $rows = $stmt->fetchAll();
    foreach ($rows as &$r) {
        $r['rating'] = (int)$r['rating'];
        if (!$r['user_name']) $r['user_name'] = 'Khách ẩn danh';
    }

    json_ok(['items' => $rows, 'pagination' => paginate($total, $page, $limit)]);
}

// ── 10. BÁO CÁO HOA HỒNG (Admin) ─────────────────────────────

function partners_reports(): void {
    require_admin();
    $month  = str_param('month', date('Y-m')); // YYYY-MM
    $page   = int_param('page', 1, 1);
    $limit  = int_param('limit', 20, 1, 100);
    $offset = ($page - 1) * $limit;

    $cs = db()->prepare("SELECT COUNT(*) FROM hv_bookings WHERE DATE_FORMAT(clicked_at,'%Y-%m') = :month AND status IN ('confirmed','completed')");
    $cs->execute([':month' => $month]);
    $total = (int)$cs->fetchColumn();

    $stmt = db()->prepare("
        SELECT
            p.uuid, p.name, p.type,
            COUNT(b.id)             AS total_bookings,
            SUM(b.amount)           AS total_revenue,
            SUM(b.commission_amount)AS total_commission,
            p.commission_rate
        FROM hv_bookings b
        JOIN hv_partners p ON p.id = b.partner_id
        WHERE DATE_FORMAT(b.clicked_at,'%Y-%m') = :month
          AND b.status IN ('confirmed','completed')
        GROUP BY p.id, p.uuid, p.name, p.type, p.commission_rate
        ORDER BY total_commission DESC
        LIMIT :limit OFFSET :offset
    ");
    $stmt->bindValue(':month',  $month,  PDO::PARAM_STR);
    $stmt->bindValue(':limit',  $limit,  PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();

    $rows = $stmt->fetchAll();
    foreach ($rows as &$r) {
        $r['total_bookings']    = (int)$r['total_bookings'];
        $r['total_revenue']     = (float)($r['total_revenue']    ?? 0);
        $r['total_commission']  = (float)($r['total_commission'] ?? 0);
        $r['commission_rate']   = (float)$r['commission_rate'];
    }

    json_ok([
        'month' => $month,
        'items' => $rows,
        'pagination' => paginate($total, $page, $limit),
    ]);
}

// ── PARTNER BOOKINGS ─────────────────────────────────────────



function partner_booking_mark_paid(string $uuid, string $bid): void {
    try { require_auth();
 } catch(Throwable $ae) {
 throw $ae; }
    try {
        $ps = db()->prepare("SELECT id as pid FROM hv_partners WHERE uuid=:u");
        $ps->execute([':u'=>$uuid]);
        $p = $ps->fetch();
        if (!$p) json_error('Không tìm thấy', 404);

        db()->prepare("
            UPDATE hv_bookings
            SET payment_status='paid', status='paid', paid_at=NOW(), updated_at=NOW()
            WHERE id=:id AND partner_id=:pid
        ")->execute([':id'=>$bid, ':pid'=>$p['pid']]);

        $bs = db()->prepare("SELECT * FROM hv_bookings WHERE id=:id");
        $bs->execute([':id'=>$bid]);
        $booking = $bs->fetch();
    } catch(Throwable $dbe) {
        json_error('DB error: '.$dbe->getMessage(), 500);
    }

    if (!empty($booking)) {
        $gEmail = $booking['guest_email'] ?? '';
        if (!$gEmail && !empty($booking['note'])) {
            preg_match('/Email: ([^\s|]+)/', $booking['note'], $em);
            $gEmail = trim($em[1] ?? '');
        }
        if ($gEmail) {
            $booking['guest_email'] = $gEmail;
            $pn = db()->prepare("SELECT name FROM hv_partners WHERE id=:pid");
            $pn->execute([':pid'=>$p['pid']]);
            $pdata = $pn->fetch();
            try {
                _send_booking_completed($booking, $pdata['name'] ?? '');
            } catch (Throwable $me) {
                error_log('[HV markPaid email] ' . $me->getMessage());
            }
        }
    }

    json_ok(null, 'Đã xác nhận thanh toán — Email thông báo đã gửi khách');
}



function _send_booking_completed(array $booking, string $partnerName): void {
    if (empty($booking['guest_email'])) return;
    try {
        $mailerPath = dirname(__DIR__) . '/PHPMailer/src/';
        require_once $mailerPath . 'Exception.php';
        require_once $mailerPath . 'PHPMailer.php';
        require_once $mailerPath . 'SMTP.php';
        $mail = new PHPMailer\PHPMailer\PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = MAIL_HOST; $mail->SMTPAuth = true;
        $mail->Username = MAIL_USER; $mail->Password = MAIL_PASS;
        $mail->SMTPSecure = 'tls'; $mail->Port = MAIL_PORT;
        $mail->CharSet = 'UTF-8';
        $mail->setFrom(MAIL_FROM, MAIL_FROM_NAME);
        $mail->addAddress($booking['guest_email']);
        $mail->isHTML(true);
        $ref    = $booking['ref_code'];
        $amount = number_format((int)($booking['payment_amount']??0));
        $mail->Subject = "[Heritage Voice] Đặt chỗ hoàn tất — Mã: {$ref}";
        ob_start();
        echo '<!DOCTYPE html><html><body style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;background:#f5f2ee;padding:20px;">';
        echo '<div style="background:#2D7A4F;padding:24px;border-radius:12px 12px 0 0;text-align:center;">';
        echo '<h2 style="color:#fff;margin:0;">✅ Đặt chỗ hoàn tất!</h2>';
        echo '<p style="color:rgba(255,255,255,.8);margin:6px 0 0;">Thanh toán đã được xác nhận bởi cơ sở</p></div>';
        echo '<div style="background:#fff;border:1px solid #e0d8d0;border-top:none;padding:24px;border-radius:0 0 12px 12px;">';
        echo '<p style="font-size:15px;color:#2C1A0E;">Cảm ơn bạn đã sử dụng Heritage Voice!</p>';
        echo '<div style="background:#D4EDDA;border-radius:10px;padding:16px;margin:16px 0;text-align:center;">';
        echo "<div style='font-size:13px;color:#155724;font-weight:700;margin-bottom:4px;'>Mã đặt chỗ</div>";
        echo "<div style='font-size:24px;font-weight:800;color:#155724;letter-spacing:3px;'>{$ref}</div>";
        if ($amount > 0) echo "<div style='font-size:14px;color:#155724;margin-top:6px;'>Đã thanh toán: <strong>{$amount}đ</strong></div>";
        echo '</div>';
        echo "<p style='font-size:13px;color:#666;'>Vui lòng xuất trình mã này khi đến <strong>{$partnerName}</strong>.</p>";
        echo '<div style="margin-top:20px;font-size:12px;color:#999;text-align:center;border-top:1px solid #f0ece8;padding-top:16px;">';
        echo 'Powered by Heritage Voice · <a href="https://tads.ctv.hanoi.vn" style="color:#8B3E18;">tads.ctv.hanoi.vn</a></div>';
        echo '</div></body></html>';
        $mail->Body = ob_get_clean();
        $mail->send();
    } catch (\Exception $e) {
        error_log('[HV BookingComplete ERROR] ' . $e->getMessage());
    }
}

function partner_bookings_list(string $uuid): void {
    $u = require_auth();
    $ps = db()->prepare("SELECT id FROM hv_partners WHERE uuid=:u");
    $ps->execute([':u'=>$uuid]);
    $p = $ps->fetch();
    if (!$p) json_error('Không tìm thấy', 404);

    $status = str_param('status');
    $where  = "partner_id=:pid";
    $params = [':pid'=>$p['id']];
    if ($status) { $where .= " AND status=:s"; $params[':s']=$status; }

    $rows = db()->prepare("SELECT * FROM hv_bookings WHERE $where ORDER BY created_at DESC LIMIT 50");
    $rows->execute($params);
    json_ok($rows->fetchAll());
}

function partner_booking_update(string $uuid, string $bid): void {
    require_auth();
    $ps = db()->prepare("SELECT p.*, p.id as pid FROM hv_partners p WHERE p.uuid=:u");
    $ps->execute([':u'=>$uuid]);
    $p = $ps->fetch();
    if (!$p) json_error('Không tìm thấy', 404);

    $b = body();
    $status = $b['status'] ?? '';
    if (!in_array($status, ['confirmed','rejected','cancelled'])) json_error('Trạng thái không hợp lệ');

    // Lấy booking trước khi update
    $bs = db()->prepare("SELECT * FROM hv_bookings WHERE id=:id AND partner_id=:pid");
    $bs->execute([':id'=>$bid, ':pid'=>$p['pid']]);
    $booking = $bs->fetch();
    if (!$booking) json_error('Không tìm thấy booking', 404);

    // Tính payment_amount từ note nếu chưa có
    $payment_amount = (int)($booking['payment_amount'] ?? 0);
    if (!$payment_amount) {
        // Parse từ note: "Đặt trước: 120,000đ"
        if (preg_match('/Đặt trước: ([\d,]+)đ/', $booking['note']??'', $m)) {
            $payment_amount = (int)str_replace(',', '', $m[1]);
        }
    }

    db()->prepare("UPDATE hv_bookings SET status=:s, payment_amount=:amt, updated_at=NOW() WHERE id=:id AND partner_id=:pid")
        ->execute([':s'=>$status, ':amt'=>$payment_amount, ':id'=>$bid, ':pid'=>$p['pid']]);

    // Nếu confirmed → gửi email QR thanh toán
    if ($status === 'confirmed' && !empty($booking['guest_email'])) {
        require_once __DIR__ . '/partner_booking.php';
        $booking['payment_amount'] = $payment_amount;
        send_payment_qr($p, $booking);
    }

    json_ok(null, 'Đã cập nhật');
}
