"""新药审批 & 指南追踪 API"""

import uuid as _uuid

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.permissions import get_current_user
from app.core.tenant_context import tenant_ctx
from app.db import get_db
from app.models.business import DrugApproval, DrugLabel, GuidelineEvidence, GuidelineVersion
from app.models.literature import GlobalLiterature, GlobalTag
from app.models.user import Tenant

router = APIRouter()


# ─── 新药审批 ───

@router.get("/drug-approvals", summary="药品审批")
async def list_approvals(
    agency: str = Query(None),
    tag_id: str = Query(None),
    year: int = Query(None, ge=2000, le=2100),
    month: int = Query(None, ge=1, le=12),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    db: AsyncSession = Depends(get_db),
):
    """FDA/NMPA/EMA 药品审批列表"""
    offset = (page - 1) * page_size
    q = select(DrugApproval).order_by(DrugApproval.approval_date.desc())
    count_q = select(func.count(DrugApproval.id))
    if agency:
        q = q.where(DrugApproval.approval_agency == agency.upper())
        count_q = count_q.where(DrugApproval.approval_agency == agency.upper())
    if tag_id:
        q = q.where(DrugApproval.cancer_type_tag_id == tag_id)
        count_q = count_q.where(DrugApproval.cancer_type_tag_id == tag_id)
    if year:
        q = q.where(func.extract('year', DrugApproval.approval_date) == year)
        count_q = count_q.where(func.extract('year', DrugApproval.approval_date) == year)
    if month:
        q = q.where(func.extract('month', DrugApproval.approval_date) == month)
        count_q = count_q.where(func.extract('month', DrugApproval.approval_date) == month)
    total = (await db.execute(count_q)).scalar() or 0
    result = await db.execute(q.offset(offset).limit(page_size))
    items = result.scalars().all()
    return {
        "items": [{"id": str(a.id), "drug_name": a.drug_name, "generic_name": a.generic_name,
                    "target": a.target, "indication": a.indication, "approval_agency": a.approval_agency,
                    "approval_date": a.approval_date.isoformat() if a.approval_date else None,
                    "approval_type": a.approval_type, "pivotal_trial_nct": a.pivotal_trial_nct,
                    "pivotal_trial_pmid": a.pivotal_trial_pmid, "source_url": a.source_url}
                  for a in items],
        "total": total,
    }


@router.get("/drug-approvals/timeline", summary="审批时间线")
async def drug_timeline(target: str = Query(..., description="靶点名称"), db: AsyncSession = Depends(get_db)):
    """某靶点的获批时间线"""
    result = await db.execute(
        select(DrugApproval).where(DrugApproval.target.ilike(f"%{target}%"))
        .order_by(DrugApproval.approval_date.asc())
    )
    items = result.scalars().all()
    return {"target": target, "approvals": [{
        "drug_name": a.drug_name, "generic_name": a.generic_name,
        "indication": a.indication, "agency": a.approval_agency,
        "date": a.approval_date.isoformat() if a.approval_date else None,
        "approval_type": a.approval_type, "target": a.target,
        "nct": a.pivotal_trial_nct,
    } for a in items]}


@router.get("/drug-approvals/target-stats", summary="靶点审批统计")
async def target_stats(
    targets: str = Query(None, description="逗号分隔的靶点列表，不传则返回所有"),
    db: AsyncSession = Depends(get_db),
):
    """返回每个靶点的药物数、审批数、最新审批日期"""
    base = select(
        DrugApproval.target,
        func.count(DrugApproval.id.distinct()).label("approval_count"),
        func.count(func.distinct(DrugApproval.drug_name)).label("drug_count"),
        func.max(DrugApproval.approval_date).label("latest_date"),
    ).where(DrugApproval.target.isnot(None), DrugApproval.target != "")

    if targets:
        target_list = [t.strip().upper() for t in targets.split(",") if t.strip()]
        if target_list:
            base = base.where(DrugApproval.target.in_(target_list))

    base = base.group_by(DrugApproval.target).order_by(func.count(DrugApproval.id).desc())

    result = await db.execute(base)
    rows = result.all()

    # Get latest drug name for each target
    stats = {}
    for r in rows:
        latest_drug = await db.scalar(
            select(DrugApproval.drug_name)
            .where(DrugApproval.target == r.target, DrugApproval.approval_date == r.latest_date)
            .limit(1)
        )
        stats[r.target] = {
            "target": r.target,
            "approval_count": r.approval_count,
            "drug_count": r.drug_count,
            "latest_date": str(r.latest_date) if r.latest_date else None,
            "latest_drug": latest_drug or "",
        }

    return {"stats": stats, "targets": list(stats.keys())}


from app.services.drug_cancer_mapper import CANCER_KEYWORD_SET, extract_cancers_from_text as _extract_cancers_from_indication


@router.get("/drug-approvals/competitive-landscape", summary="靶点竞争格局")
async def competitive_landscape(
    target: str = Query(None, description="筛选特定靶点"),
    db: AsyncSession = Depends(get_db),
):
    """返回每个靶点的药物覆盖癌种矩阵"""
    from collections import defaultdict

    q = select(DrugApproval).where(
        DrugApproval.target.isnot(None),
        DrugApproval.target != "",
    ).order_by(DrugApproval.approval_date.asc())

    if target:
        q = q.where(DrugApproval.target == target.upper())

    result = await db.execute(q)
    approvals = result.scalars().all()

    # ── Build label-based cancer lookup from DrugLabel.parsed_indications ──
    r = await db.execute(
        select(DrugLabel).where(DrugLabel.parsed_indications.isnot(None))
    )
    label_map: dict[str, set[str]] = defaultdict(set)
    for lb in r.scalars().all():
        parsed = lb.parsed_indications or {}
        for c in parsed.get("cancers", []):
            ct = c.get("cancer_type")
            if ct:
                if lb.generic_name:
                    label_map[lb.generic_name.lower()].add(ct)
                if lb.drug_name:
                    label_map[lb.drug_name.lower()].add(ct)

    # 按 target 聚合
    groups: dict[str, dict] = {}
    drugs_seen: dict[str, set[str]] = defaultdict(set)

    for a in approvals:
        tgt = a.target.upper()
        if tgt not in groups:
            groups[tgt] = {
                "target": tgt,
                "drugs": {},
                "cancers": set(),
                "first_year": None,
                "total_approvals": 0,
            }

        g = groups[tgt]
        g["total_approvals"] += 1
        year = a.approval_date.year if a.approval_date else None
        if year and (g["first_year"] is None or year < g["first_year"]):
            g["first_year"] = year

        # 收集该靶点的药物及各自覆盖癌种
        drug_key = a.generic_name.lower()
        if drug_key not in g["drugs"]:
            g["drugs"][drug_key] = {
                "drug_name": a.drug_name,
                "generic_name": a.generic_name,
                "first_year": year,
                "cancers": set(),
            }

        # 从 indication 文本中匹配癌症关键词
        indication_cancers = set(
            _extract_cancers_from_indication(a.indication)
        )
        g["drugs"][drug_key]["cancers"].update(indication_cancers)
        g["cancers"].update(indication_cancers)

        # 从 Label parsed_indications 补充（结构化了适应症→癌种映射）
        for name_key in [a.generic_name.lower(), a.drug_name.lower()]:
            if name_key in label_map:
                g["cancers"].update(label_map[name_key])
                g["drugs"][drug_key]["cancers"].update(label_map[name_key])

    # 序列化
    landscape = []
    for tgt in sorted(groups.keys()):
        g = groups[tgt]
        landscape.append({
            "target": tgt,
            "drug_count": len(g["drugs"]),
            "total_approvals": g["total_approvals"],
            "first_year": g["first_year"],
            "cancers": sorted(g["cancers"]),
            "drugs": [
                {
                    "drug_name": d["drug_name"],
                    "generic_name": d["generic_name"],
                    "first_year": d["first_year"],
                    "cancers": sorted(d["cancers"]),
                }
                for d in sorted(g["drugs"].values(), key=lambda x: x["first_year"] or 0)
            ],
        })

    # 返回全部 target 列表（做筛选器用）
    all_targets = sorted(groups.keys())
    return {"landscape": landscape, "targets": all_targets}


@router.get("/drug-approvals/{aid}/label", summary="药品标签详情")
async def drug_label_detail(aid: str, db: AsyncSession = Depends(get_db)):
    """返回某个审批记录的 FDA Label 详情（适应症原文、作用机制、临床试验、关联文献）"""
    try:
        approval = await db.get(DrugApproval, _uuid.UUID(aid))
    except ValueError:
        raise HTTPException(400, "无效 ID")
    if not approval:
        raise HTTPException(404)

    # 按 drug_name 或 generic_name 匹配 label（按 effective_time 降序取最新的）
    r = await db.execute(
        select(DrugLabel)
        .where(
            or_(DrugLabel.drug_name == approval.drug_name, DrugLabel.generic_name == approval.generic_name)
        )
        .order_by(DrugLabel.effective_time.desc())
        .limit(5)
    )
    labels = r.scalars().all()
    if not labels:
        return {"approval": {"id": aid, "drug_name": approval.drug_name}, "labels": []}

    # ── 收集所有 label 中的 NCT，查 GlobalLiterature ──
    all_ncts = set()
    for lb in labels:
        if lb.nct_numbers:
            all_ncts.update(lb.nct_numbers)
    clinical_trials = []
    if all_ncts:
        # 按引用数排序，取前 10 条匹配 NCT 的文献
        lit_r = await db.execute(
            select(GlobalLiterature)
            .where(
                GlobalLiterature.trial_reg.isnot(None),
                GlobalLiterature.trial_reg['nct'].astext.in_(all_ncts),
            )
            .order_by(GlobalLiterature.cited_by_count.desc())
            .limit(10)
        )
        for lit in lit_r.scalars().all():
            authors_list = lit.authors or []
            first_author = authors_list[0].get("family", "") if authors_list else ""
            clinical_trials.append({
                "pmid": lit.pmid,
                "title": lit.title,
                "journal": lit.journal,
                "cited_by_count": lit.cited_by_count or 0,
                "pub_date": lit.pub_date.isoformat() if lit.pub_date else None,
                "first_author": first_author,
            })

    return {
        "approval": {
            "id": aid,
            "drug_name": approval.drug_name,
            "generic_name": approval.generic_name,
            "target": approval.target,
            "indication": approval.indication,
        },
        "labels": [
            {
                "set_id": l.set_id,
                "effective_time": l.effective_time,
                "indications_and_usage": l.indications_and_usage,
                "mechanism_of_action": l.mechanism_of_action,
                "clinical_studies": l.clinical_studies,
                "warnings": l.warnings,
                "adverse_reactions": l.adverse_reactions,
                "nct_numbers": l.nct_numbers,
                "parsed_indications": l.parsed_indications,
            }
            for l in labels
        ],
        "clinical_trials": clinical_trials,
    }


@router.get("/drug-approvals/{aid}/guidelines", summary="药品关联指南")
async def drug_related_guidelines(aid: str, db: AsyncSession = Depends(get_db)):
    """查询引用了该药品所在癌种的指南列表"""
    try:
        approval = await db.get(DrugApproval, _uuid.UUID(aid))
    except ValueError:
        raise HTTPException(400, "无效 ID")
    if not approval:
        raise HTTPException(404)

    # 优先用 cancer_type_tag_id，否则从适应症文本提取癌种关键词匹配
    tag_ids = []
    if approval.cancer_type_tag_id:
        tag_ids = [approval.cancer_type_tag_id]
    else:
        cancers = _extract_cancers_from_indication(approval.indication)
        if cancers:
            conditions = [GlobalTag.name_en.ilike(f"%{c}%") for c in cancers]
            r = await db.execute(
                select(GlobalTag.id).where(
                    GlobalTag.tag_category == "cancer",
                    GlobalTag.is_active == True,
                    or_(*conditions),
                ).limit(5)
            )
            tag_ids = [row[0] for row in r]

    if not tag_ids:
        return {"cancer_type": None, "guidelines": []}

    tag = await db.get(GlobalTag, tag_ids[0]) if approval.cancer_type_tag_id else None
    r = await db.execute(
        select(
            GuidelineVersion.id,
            GuidelineVersion.guideline_source,
            GuidelineVersion.version,
            GuidelineVersion.publish_date,
            GuidelineVersion.change_summary_zh,
            func.count(GuidelineEvidence.id).label("evidence_count"),
        )
        .outerjoin(GuidelineEvidence, GuidelineEvidence.guideline_version_id == GuidelineVersion.id)
        .where(GuidelineVersion.cancer_type_tag_id.in_(tag_ids))
        .group_by(GuidelineVersion.id)
        .order_by(GuidelineVersion.publish_date.desc())
    )
    return {
        "cancer_type": tag.name_zh or tag.name_en if tag else None,
        "guidelines": [
            {
                "id": str(row.id),
                "source": row.guideline_source,
                "version": row.version,
                "publish_date": row.publish_date.isoformat() if row.publish_date else None,
                "change_summary_zh": row.change_summary_zh,
                "evidence_count": row.evidence_count,
            }
            for row in r
        ],
    }


@router.get("/guidelines/{gid}/drugs", summary="指南关联药品")
async def guideline_related_drugs(
    gid: str,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
):
    """查询某指南对应癌种的相关药品，支持分页"""
    try:
        gv = await db.get(GuidelineVersion, _uuid.UUID(gid))
    except ValueError:
        raise HTTPException(400, "无效 ID")
    if not gv:
        raise HTTPException(404)

    count_q = select(func.count(DrugApproval.id)).where(
        DrugApproval.cancer_type_tag_id == gv.cancer_type_tag_id
    )
    total = (await db.execute(count_q)).scalar() or 0

    offset = (page - 1) * page_size
    r = await db.execute(
        select(DrugApproval)
        .where(DrugApproval.cancer_type_tag_id == gv.cancer_type_tag_id)
        .order_by(DrugApproval.approval_date.desc())
        .offset(offset)
        .limit(page_size)
    )
    return {
        "drugs": [
            {
                "id": str(d.id),
                "drug_name": d.drug_name,
                "generic_name": d.generic_name,
                "target": d.target,
                "indication": d.indication,
                "approval_agency": d.approval_agency,
                "approval_date": d.approval_date.isoformat() if d.approval_date else None,
            }
            for d in r.scalars().all()
        ],
        "total": total,
    }


# ─── 靶点订阅 ───

class TargetSubscribe(BaseModel):
    target: str


@router.post("/subscribe-target", summary="订阅靶点审批通知")
async def subscribe_target(
    req: TargetSubscribe,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    """订阅某个靶点（如 EGFR, ALK）的审批通知"""
    raw_tid = tenant_ctx.get()
    if not raw_tid:
        return {"status": "skipped", "targets": []}
    t = await db.get(Tenant, _uuid.UUID(raw_tid))
    if not t:
        raise HTTPException(404)
    settings = dict(t.settings or {})
    key = f"approval_subs:{user['sub']}"
    subs = set(settings.get(key, []))
    if req.target in subs:
        return {"status": "already_subscribed", "targets": list(subs)}
    subs.add(req.target)
    settings[key] = list(subs)
    t.settings = settings
    await db.commit()
    return {"status": "subscribed", "targets": list(subs)}


@router.get("/subscribed-targets", summary="已订阅靶点列表")
async def list_subscribed_targets(
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    raw_tid = tenant_ctx.get()
    if not raw_tid:
        return {"targets": []}
    t = await db.get(Tenant, _uuid.UUID(raw_tid))
    if not t:
        return {"targets": []}
    key = f"approval_subs:{user['sub']}"
    subs = (t.settings or {}).get(key, [])
    return {"targets": subs}


@router.post("/unsubscribe-target", summary="取消订阅靶点")
async def unsubscribe_target(
    req: TargetSubscribe,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    raw_tid = tenant_ctx.get()
    if not raw_tid:
        return {"status": "skipped"}
    t = await db.get(Tenant, _uuid.UUID(raw_tid))
    if not t:
        raise HTTPException(404)
    settings = dict(t.settings or {})
    key = f"approval_subs:{user['sub']}"
    subs = set(settings.get(key, []))
    subs.discard(req.target)
    settings[key] = list(subs)
    t.settings = settings
    await db.commit()
    return {"status": "unsubscribed", "targets": list(subs)}


# ─── 指南追踪 ───

@router.get("/guidelines", summary="指南列表")
async def list_guidelines(
    source: str = Query(None),
    tag_id: str = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
):
    """临床指南版本列表"""
    offset = (page - 1) * page_size
    q = select(GuidelineVersion).order_by(GuidelineVersion.publish_date.desc())
    count_q = select(func.count(GuidelineVersion.id))
    if source:
        q = q.where(GuidelineVersion.guideline_source == source.upper())
        count_q = count_q.where(GuidelineVersion.guideline_source == source.upper())
    if tag_id:
        q = q.where(GuidelineVersion.cancer_type_tag_id == tag_id)
        count_q = count_q.where(GuidelineVersion.cancer_type_tag_id == tag_id)
    total = (await db.execute(count_q)).scalar() or 0
    result = await db.execute(q.offset(offset).limit(page_size))
    items = result.scalars().all()
    return {
        "items": [{"id": str(g.id), "source": g.guideline_source, "version": g.version,
                    "publish_date": g.publish_date.isoformat() if g.publish_date else None,
                    "change_summary_zh": g.change_summary_zh, "pdf_url": g.pdf_url,
                    "source_url": g.source_url}
                  for g in items],
        "total": total,
    }


@router.get("/guidelines/changes/{source}/{from_ver}/{to_ver}", summary="版本对比")
async def guideline_changes(
    source: str, from_ver: str, to_ver: str,
    db: AsyncSession = Depends(get_db),
):
    """指南版本变更对比（含证据级别对比）"""
    r1 = await db.execute(select(GuidelineVersion).where(
        GuidelineVersion.guideline_source == source.upper(),
        GuidelineVersion.version == from_ver,
    ))
    r2 = await db.execute(select(GuidelineVersion).where(
        GuidelineVersion.guideline_source == source.upper(),
        GuidelineVersion.version == to_ver,
    ))
    v1, v2 = r1.scalar(), r2.scalar()
    if not v1 or not v2:
        return {
            "from": {"version": v1.version, "date": str(v1.publish_date), "summary": v1.change_summary_zh} if v1 else None,
            "to": {"version": v2.version, "date": str(v2.publish_date), "summary": v2.change_summary_zh} if v2 else None,
        }

    # 证据级别对比
    ev1 = (await db.execute(
        select(GuidelineEvidence, GlobalLiterature.title, GlobalLiterature.pmid)
        .join(GlobalLiterature)
        .where(GuidelineEvidence.guideline_version_id == v1.id)
    )).all()
    ev2 = (await db.execute(
        select(GuidelineEvidence, GlobalLiterature.title, GlobalLiterature.pmid)
        .join(GlobalLiterature)
        .where(GuidelineEvidence.guideline_version_id == v2.id)
    )).all()

    def ev_key(e: GuidelineEvidence) -> str:
        return str(e.literature_id)

    map1 = {ev_key(ev): (ev, title, pmid) for ev, title, pmid in ev1}
    map2 = {ev_key(ev): (ev, title, pmid) for ev, title, pmid in ev2}

    lit_ids_1 = set(map1.keys())
    lit_ids_2 = set(map2.keys())
    common_ids = lit_ids_1 & lit_ids_2

    def fmt_ev(ev: GuidelineEvidence, title: str, pmid: int) -> dict:
        return {
            "recommendation": ev.recommendation,
            "level": ev.recommendation_level,
            "treatment_line": ev.treatment_line,
            "pmid": pmid,
            "title": title[:120] if title else "",
        }

    added = [fmt_ev(ev, t, p) for ev, t, p in (map2[lid] for lid in lit_ids_2 - lit_ids_1)]
    removed = [fmt_ev(ev, t, p) for ev, t, p in (map1[lid] for lid in lit_ids_1 - lit_ids_2)]

    changed = []
    for lid in common_ids:
        ev_a, t_a, p_a = map1[lid]
        ev_b, t_b, p_b = map2[lid]
        changes = {}
        if ev_a.recommendation != ev_b.recommendation:
            changes["recommendation"] = [ev_a.recommendation, ev_b.recommendation]
        if ev_a.recommendation_level != ev_b.recommendation_level:
            changes["level"] = [ev_a.recommendation_level, ev_b.recommendation_level]
        if ev_a.treatment_line != ev_b.treatment_line:
            changes["treatment_line"] = [ev_a.treatment_line, ev_b.treatment_line]
        if changes:
            changed.append({**fmt_ev(ev_b, t_b, p_b), "changes": changes})

    return {
        "from": {"version": v1.version, "date": str(v1.publish_date), "summary": v1.change_summary_zh},
        "to": {"version": v2.version, "date": str(v2.publish_date), "summary": v2.change_summary_zh},
        "evidence_diff": {"added": added, "removed": removed, "changed": changed},
    }


@router.get("/guidelines/quick-index", summary="快速索引")
async def guideline_quick_index(
    cancer_type: str = Query("NSCLC"),
    treatment_line: str = Query(None),
    db: AsyncSession = Depends(get_db),
):
    """快速索引：按癌种+治疗线数查找当前标准方案"""
    # 找到所有匹配的癌种标签（同时匹配 path 和 name_zh）
    tags = (await db.execute(
        select(GlobalTag).where(
            or_(GlobalTag.path.ilike(f"%{cancer_type}%"), GlobalTag.name_zh.ilike(f"%{cancer_type}%"))
        )
    )).scalars().all()
    if not tags:
        return {"standard_treatments": [], "note": "No matching cancer type tag found"}

    # 收集所有匹配标签下的指南（可能有种子重复标签）
    tag_ids = list({t.id for t in tags})
    guidelines = (await db.execute(
        select(GuidelineVersion)
        .where(GuidelineVersion.cancer_type_tag_id.in_(tag_ids))
        .order_by(GuidelineVersion.publish_date.desc())
    )).scalars().all()

    # 取 cancer_type 显示名（用第一个有 name_zh 的标签）
    cancer_name = next((t.name_zh for t in tags if t.name_zh), tags[0].name_zh or cancer_type)

    # 查找相关证据文献
    treatments = []
    for g in guidelines[:5]:  # 取最新5个指南
        evidence = (await db.execute(
            select(GuidelineEvidence, GlobalLiterature.title)
            .join(GlobalLiterature)
            .where(GuidelineEvidence.guideline_version_id == g.id)
        )).all()
        for ev, lit_title in evidence:
            if treatment_line and ev.treatment_line != treatment_line: continue
            treatments.append({
                "guideline": f"{g.guideline_source} {g.version}",
                "recommendation": ev.recommendation,
                "level": ev.recommendation_level,
                "line": ev.treatment_line,
                "evidence": lit_title[:100],
            })

    return {
        "cancer_type": cancer_name,
        "standard_treatments": treatments,
        "guidelines_referenced": [f"{g.guideline_source} {g.version} ({g.publish_date})" for g in guidelines[:5]],
    }


@router.get("/guidelines/{gid}/evidence", summary="证据列表")
async def guideline_evidence_list(gid: str, db: AsyncSession = Depends(get_db)):
    """某指南版本引用的证据文献链"""
    import uuid as _uuid
    try: gid_uuid = _uuid.UUID(gid)
    except ValueError: raise HTTPException(400)
    result = await db.execute(
        select(GuidelineEvidence, GlobalLiterature.title, GlobalLiterature.journal, GlobalLiterature.pmid)
        .join(GlobalLiterature)
        .where(GuidelineEvidence.guideline_version_id == gid_uuid)
        .order_by(GuidelineEvidence.recommendation_level.asc())
    )
    items = [{"recommendation": ev.recommendation, "level": ev.recommendation_level,
               "treatment_line": ev.treatment_line, "pmid": pmid, "title": title[:120], "journal": journal}
             for ev, title, journal, pmid in result]
    return {"items": items, "total": len(items)}
