# -*- coding: utf-8 -*-
"""
租户管理后台 — 维客线索管理路由。

端点清单：
- GET   /api/tenant/customers/search              — 客户搜索（keyword + site_id）
- GET   /api/tenant/customers/{member_id}/clues    — 线索列表（source / is_hidden 筛选）
- PATCH /api/tenant/clues/{id}                     — 编辑线索
- DELETE /api/tenant/clues/{id}                    — 物理删除线索
- PATCH /api/tenant/clues/{id}/visibility          — 切换隐藏/显示

需求: 9.1-9.4, 10.1, 11.1-11.3, 12.2-12.3, 13.1-13.4

AI_CHANGELOG
- 2026-03-23 21:00:00 | Prompt: P20260323-210000（根治 tenant_admin managed_site_ids 限制）| Direct cause：JWT managed_site_ids 静态签发，新建店铺后所有端点受限 | Summary：_get_clue_with_site_check 签名改为接受 admin: CurrentTenantAdmin；search_customers 用 get_effective_site_ids；list_customer_clues 用 site_filter_clause(admin=admin)；三个调用点改传 admin | Verify：维客线索管理覆盖新建店铺
"""

from __future__ import annotations

import logging
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query, status

from app.auth.tenant_admins import (
    CurrentTenantAdmin,
    get_effective_site_ids,
    require_tenant_admin,
    site_filter_clause,
)
from app.database import get_connection, get_etl_readonly_connection
from app.schemas.tenant_clues import (
    ClueEditRequest,
    ClueListItem,
    ClueVisibilityRequest,
    CustomerSearchItem,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/tenant", tags=["维客线索管理"])


def _mask_mobile(mobile: str | None) -> str | None:
    """手机号脱敏：中间 4 位替换为 ****，如 138****1234。"""
    if not mobile or len(mobile) < 7:
        return mobile
    return mobile[:3] + "****" + mobile[7:]


def _get_clue_with_site_check(clue_id: int, admin: CurrentTenantAdmin):
    """
    查询线索并校验 site_id 是否在管辖范围内。

    不在管辖范围或不存在均返回 404（避免泄露线索存在性）。
    返回 (id, site_id, member_id, category, summary, detail,
           recorded_by_name, source, recorded_at, is_hidden)。
    """
    site_sql, site_params = site_filter_clause(admin=admin)
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                f"""
                SELECT id, site_id, member_id, category, summary, detail,
                       recorded_by_name, source, recorded_at::text, is_hidden
                  FROM public.member_retention_clue
                 WHERE id = %s AND {site_sql}
                """,
                (clue_id, *site_params),
            )
            row = cur.fetchone()
    finally:
        conn.close()

    if row is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="线索不存在")
    return row


# ── GET /api/tenant/customers/search ──────────────────────


@router.get("/customers/search")
async def search_customers(
    keyword: str = Query(..., min_length=1, description="搜索关键词（姓名模糊/手机号精确）"),
    site_id: Optional[int] = Query(None, description="指定门店 ID 筛选"),
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """
    客户搜索：在管辖门店范围内搜索 v_dim_member。

    nickname 模糊匹配 OR mobile 精确匹配，scd2_is_current=1。
    手机号脱敏返回。
    """
    # 确定要搜索的门店列表
    # [CHANGE P20260323-210000] intent: 使用 get_effective_site_ids 统一获取有效 site_ids
    effective_ids = get_effective_site_ids(admin)
    if site_id is not None:
        if site_id not in effective_ids:
            return {"items": []}
        search_site_ids = [site_id]
    else:
        search_site_ids = effective_ids

    if not search_site_ids:
        return {"items": []}

    # 逐 site_id 查询 FDW（RLS 要求逐个设置 current_site_id）
    all_items: list[dict] = []
    for sid in search_site_ids:
        try:
            etl_conn = get_etl_readonly_connection(sid)
            try:
                with etl_conn.cursor() as cur:
                    cur.execute(
                        """
                        SELECT member_id, nickname, mobile
                          FROM app.v_dim_member
                         WHERE scd2_is_current = 1
                           AND (nickname ILIKE %s OR mobile = %s)
                         LIMIT 50
                        """,
                        (f"%{keyword}%", keyword),
                    )
                    for row in cur.fetchall():
                        all_items.append(
                            CustomerSearchItem(
                                member_id=row[0],
                                nickname=row[1],
                                mobile_masked=_mask_mobile(row[2]),
                                site_id=sid,
                            ).model_dump(by_alias=True)
                        )
            finally:
                etl_conn.close()
        except Exception:
            logger.warning("v_dim_member 搜索失败（site_id=%s）", sid, exc_info=True)

    # 补充 site_name
    if all_items:
        site_ids_set = list({item.get("siteId") for item in all_items if item.get("siteId")})
        if site_ids_set:
            conn = get_connection()
            try:
                with conn.cursor() as cur:
                    placeholders = ", ".join(["%s"] * len(site_ids_set))
                    cur.execute(
                        f"SELECT site_id, site_name FROM biz.sites WHERE site_id IN ({placeholders})",
                        tuple(site_ids_set),
                    )
                    site_name_map = {r[0]: r[1] for r in cur.fetchall()}
            finally:
                conn.close()

            for item in all_items:
                sid_val = item.get("siteId")
                if sid_val and sid_val in site_name_map:
                    item["siteName"] = site_name_map[sid_val]

    return {"items": all_items}


# ── GET /api/tenant/customers/{member_id}/clues ───────────


@router.get("/customers/{member_id}/clues")
async def list_customer_clues(
    member_id: int,
    source: Optional[str] = Query(None, description="按来源筛选：manual/ai_consumption/ai_note"),
    is_hidden: Optional[bool] = Query(None, description="按隐藏状态筛选"),
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """返回该客户在管辖门店范围内的全部线索，支持 source 和 is_hidden 筛选。"""
    site_sql, site_params = site_filter_clause(admin=admin)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            where_parts = [f"{site_sql}", "member_id = %s"]
            params: list = list(site_params) + [member_id]

            if source is not None:
                where_parts.append("source = %s")
                params.append(source)

            if is_hidden is not None:
                where_parts.append("is_hidden = %s")
                params.append(is_hidden)

            where_clause = " AND ".join(where_parts)

            cur.execute(
                f"""
                SELECT id, category, summary, detail,
                       recorded_by_name, source, recorded_at::text, is_hidden
                  FROM public.member_retention_clue
                 WHERE {where_clause}
                 ORDER BY recorded_at DESC
                """,
                tuple(params),
            )
            rows = cur.fetchall()
    finally:
        conn.close()

    items = [
        ClueListItem(
            id=r[0], category=r[1], summary=r[2], detail=r[3],
            recorded_by_name=r[4], source=r[5], recorded_at=r[6], is_hidden=r[7],
        ).model_dump(by_alias=True)
        for r in rows
    ]
    return {"items": items}


# ── PATCH /api/tenant/clues/{id} ──────────────────────────


@router.patch("/clues/{clue_id}")
async def edit_clue(
    clue_id: int,
    body: ClueEditRequest,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """编辑线索 category/summary/detail。校验 category 枚举和 summary 长度。"""
    # 先校验线索存在且在管辖范围内
    _get_clue_with_site_check(clue_id, admin)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                UPDATE public.member_retention_clue
                   SET category = %s, summary = %s, detail = %s
                 WHERE id = %s
                """,
                (body.category.value, body.summary, body.detail, clue_id),
            )
        conn.commit()
    except Exception:
        conn.rollback()
        logger.error("编辑线索失败（clue_id=%s）", clue_id, exc_info=True)
        raise HTTPException(status_code=500, detail="编辑操作失败")
    finally:
        conn.close()

    return {"message": "更新成功"}


# ── DELETE /api/tenant/clues/{id} ─────────────────────────


@router.delete("/clues/{clue_id}")
async def delete_clue(
    clue_id: int,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """物理删除线索。线索不存在或不在管辖范围返回 404。"""
    _get_clue_with_site_check(clue_id, admin)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                "DELETE FROM public.member_retention_clue WHERE id = %s",
                (clue_id,),
            )
        conn.commit()
    except Exception:
        conn.rollback()
        logger.error("删除线索失败（clue_id=%s）", clue_id, exc_info=True)
        raise HTTPException(status_code=500, detail="删除操作失败")
    finally:
        conn.close()

    return {"message": "删除成功"}


# ── PATCH /api/tenant/clues/{id}/visibility ───────────────


@router.patch("/clues/{clue_id}/visibility")
async def toggle_clue_visibility(
    clue_id: int,
    body: ClueVisibilityRequest,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """切换线索 is_hidden 状态。"""
    _get_clue_with_site_check(clue_id, admin)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                UPDATE public.member_retention_clue
                   SET is_hidden = %s
                 WHERE id = %s
                """,
                (body.is_hidden, clue_id),
            )
        conn.commit()
    except Exception:
        conn.rollback()
        logger.error("切换线索可见性失败（clue_id=%s）", clue_id, exc_info=True)
        raise HTTPException(status_code=500, detail="操作失败")
    finally:
        conn.close()

    return {"message": "更新成功"}
