"""query 引擎。

排序规则（固定，对外公开）:
    profile.updated_at DESC —— 新资料 / 新活跃的人优先

红线:
    - 不返回手机号 / session / phone_hash 等任何 PII
    - where 不能为空（防一次性拉全库）
    - limit 范围 [1, 100]

注：付费排序语义（原 exposure 系统）已在 2026-04-23 砍掉；
    付费逻辑移到 mail.py（信封 + 触达），不再影响查询排序。
"""

from __future__ import annotations

from dataclasses import dataclass

from .db import get_conn
from .dsl import DSLError, parse as parse_dsl

LIMIT_DEFAULT = 50
LIMIT_MAX = 100


class QueryError(Exception):
    def __init__(self, code: str, msg: str):
        super().__init__(msg)
        self.code = code


@dataclass
class QueryHit:
    user_id: str
    gender: str | None
    age: int | None
    city: str | None
    tags: list[str]
    bio: str | None
    height: int | None
    education: str | None
    verified: bool  # PRD-001 决策点 #5：结果带实名标签


@dataclass
class QueryResult:
    results: list[QueryHit]
    total_hits: int


def run(caller_user_id: str, where: str, limit: int = LIMIT_DEFAULT) -> QueryResult:
    if limit < 1 or limit > LIMIT_MAX:
        raise QueryError("invalid_value", f"limit 范围 [1, {LIMIT_MAX}]，收到 {limit}")

    # FEAT-019：业务层限流（query_min=30/60s, query_hour=500/3600s）
    from . import rate_limit as rl

    try:
        rl.check_group(caller_user_id, "query")
    except rl.RateLimitExceeded as e:
        raise QueryError("rate_limited", str(e)) from e

    try:
        parsed = parse_dsl(where)
    except DSLError as e:
        raise QueryError(e.code, str(e)) from e

    sql_where = _prefix_columns(parsed.sql)

    sql = f"""
        SELECT
            p.user_id, p.gender, p.age, p.city, p.tags, p.bio,
            p.height, p.education,
            (u.verified_at IS NOT NULL) AS verified
        FROM profiles p
        JOIN users u ON u.id = p.user_id
        WHERE u.deleted_at IS NULL
          AND u.id != ?
          AND p.gender IS NOT NULL
          AND p.age IS NOT NULL
          AND p.city IS NOT NULL
          AND ({sql_where})
        ORDER BY p.updated_at DESC
        LIMIT ?
    """
    params = [caller_user_id, *parsed.params, limit]

    results: list[QueryHit] = []

    with get_conn() as conn:
        for row in conn.execute(sql, params):
            results.append(
                QueryHit(
                    user_id=row["user_id"],
                    gender=row["gender"],
                    age=row["age"],
                    city=row["city"],
                    tags=_tags_from_db(row["tags"]),
                    bio=row["bio"],
                    height=row["height"],
                    education=row["education"],
                    verified=bool(row["verified"]),
                )
            )

    return QueryResult(results=results, total_hits=len(results))


def _prefix_columns(sql: str) -> str:
    """把 DSL 生成的 `gender = ?` 变成 `p.gender = ?`；verified 是虚拟字段。"""
    import re as _re

    # verified 是虚拟字段：DSL 生成 `verified = ?`，翻译成 users.verified_at IS NOT NULL 的 bool
    sql = _re.sub(r"\bverified\b", "(u.verified_at IS NOT NULL)", sql)

    for col in ("gender", "age", "city", "tags", "height", "education"):
        sql = _re.sub(rf"\b{col}\b", f"p.{col}", sql)
    return sql


def _tags_from_db(raw: str | None) -> list[str]:
    if not raw:
        return []
    return [t for t in raw.strip("|").split("|") if t]
