def _conversation_rows(
    message_path: Path,
    account: str,
    users: dict[str, str],
    conversations: dict[str, str],
    query: str,
    limit: int,
    offset: int = 0,
) -> tuple[list[dict[str, Any]], int, int]:
    index = _message_index(message_path)
    ordered = index["ordered"]
    needle = str(query or "").strip().casefold()
    offset = max(0, int(offset))
    candidates = ordered if needle else ordered[offset:offset + limit]
    _ensure_previews(message_path, index, candidates)
    result = []
    matching = 0
    for conversation_id in candidates:
        name = _conversation_name(account, conversation_id, users, conversations)
        preview = index["previews"][conversation_id]
        if needle and needle not in f"{name} {conversation_id} {preview['text']}".casefold():
            continue
        if needle:
            matching += 1
            if matching <= offset:
                continue
        group = index["conversations"][conversation_id]
        result.append({
            "id": conversation_id, "name": name, "kind": _conversation_kind(conversation_id),
            "messageCount": len(group["rows"]), "lastTime": _format_time(group["lastTimestamp"]),
            "lastTimestamp": group["lastTimestamp"], "preview": preview["text"][:160],
            "lastDirection": "发出" if preview["sender"] == account else "收到",
        })
        if len(result) >= limit:
            break
    return result, len(ordered), index["total_messages"]


def _messages(
    message_path: Path,
    account: str,
    conversation_id: str,
    users: dict[str, str],
    limit: int,
    offset: int = 0,
) -> list[dict[str, Any]]:
    if not conversation_id:
        return []
    index = _message_index(message_path)
    rows = (index["conversations"].get(conversation_id) or {}).get("rows", [])
    offset = max(0, int(offset))
    limit = max(1, min(int(limit), 1000))
    end = max(0, len(rows) - offset)
    row_ids = list(rows[max(0, end - limit):end])
    revision = index["revision"]
    key = (revision[0], conversation_id, limit, offset)
    records = _cached("messages", key, revision, lambda: _read_message_rows(message_path, index, row_ids))
    result = []
    for row_id in row_ids:
        item = records.get(row_id)
        if item is None:
            continue
        sender_id = str(item.get("sender_id") or "")
        try:
            timestamp = float(item.get("send_time") or 0)
            if not math.isfinite(timestamp):
                timestamp = 0.0
        except (ValueError, TypeError, OverflowError):
            timestamp = 0.0
        result.append({
            "id": str(item.get("server_id") or item.get("client_id") or ""),
            "senderId": sender_id,
            "sender": users.get(sender_id) or ("当前账号" if sender_id == account else sender_id),
            "direction": "outbound" if sender_id == account else "inbound",
            "time": _format_time(timestamp), "timestamp": timestamp,
            "type": get_msg_type_name(item.get("content_type")),
            "content": _content_label(item.get("content"), item.get("content_type")),
        })
    return result


