"""MDT 肿瘤多学科协作 API — 使用专用 SQL 表"""

import re
import uuid
from datetime import datetime

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

from app.compat import UTC
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.literature import GlobalLiterature, GlobalLiteratureTag, GlobalTag
from app.schemas.literature import cap_pub_date
from app.models.mdt import MDTCase, MDTDecision, MDTEvidence
from app.models.user import User

router = APIRouter()


def uid(u: dict) -> uuid.UUID:
    return uuid.UUID(u["sub"])


# ─── Schemas ───


class MDTCaseCreate(BaseModel):
    title: str
    patient_summary: str
    cancer_type: str | None = None


class MDTDecisionCreate(BaseModel):
    conclusion: str
    participants: list[str] = []
    meeting_date: str | None = None  # YYYY-MM-DD


class AddLiteratureReq(BaseModel):
    pmid: int
    relevance_note: str | None = None


# ─── Helpers ───


def _to_case_json(c: MDTCase, decisions: list[MDTDecision] | None = None, evidence_count: int = 0) -> dict:
    return {
        "id": str(c.id),
        "title": c.title,
        "patient_summary": c.patient_summary,
        "cancer_type": c.cancer_type,
        "status": c.status,
        "created_by": str(c.created_by) if c.created_by else None,
        "created_at": c.created_at.isoformat() if c.created_at else None,
        "decisions": [_to_decision_json(d) for d in (decisions or [])],
        "evidence_count": evidence_count,
    }


def _to_decision_json(d: MDTDecision) -> dict:
    return {
        "id": str(d.id),
        "conclusion": d.conclusion,
        "meeting_date": d.meeting_date.isoformat() if d.meeting_date else None,
        "participants": d.participants or [],
        "decided_by": str(d.decided_by) if d.decided_by else None,
        "decided_at": d.decided_at.isoformat() if d.decided_at else None,
    }


def _to_evidence_json(e: MDTEvidence) -> dict:
    return {
        "id": str(e.id),
        "case_id": str(e.case_id),
        "decision_id": str(e.decision_id) if e.decision_id else None,
        "literature_id": str(e.literature_id) if e.literature_id else None,
        "pmid": e.pmid,
        "citation": e.citation,
        "relevance_note": e.relevance_note,
        "added_by": str(e.added_by) if e.added_by else None,
        "created_at": e.created_at.isoformat() if e.created_at else None,
    }


# ─── Endpoints ───


@router.get("/cases", summary="病例列表")
async def list_cases(
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
):
    """获取当前租户的 MDT 病例列表"""
    tid = tenant_ctx.get()
    if not tid:
        return {"items": [], "total": 0, "page": page, "page_size": page_size}

    tid_uuid = uuid.UUID(tid)

    # Count total
    count_q = select(func.count(MDTCase.id)).where(MDTCase.tenant_id == tid_uuid)
    total = (await db.execute(count_q)).scalar() or 0

    # Fetch page with decisions
    q = (
        select(MDTCase)
        .where(MDTCase.tenant_id == tid_uuid)
        .options(selectinload(MDTCase.decisions))
        .order_by(MDTCase.created_at.desc())
        .offset((page - 1) * page_size)
        .limit(page_size)
    )
    rows = (await db.execute(q)).scalars().all()

    items = []
    for c in rows:
        dec_count = len(c.decisions) if c.decisions else 0
        items.append(_to_case_json(c, decisions=c.decisions, evidence_count=dec_count))

    return {"items": items, "total": total, "page": page, "page_size": page_size}


@router.post("/cases", summary="创建病例")
async def create_case(
    req: MDTCaseCreate,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        return {"status": "skipped", "note": "请先创建或加入团队"}

    case = MDTCase(
        tenant_id=uuid.UUID(tid),
        title=req.title,
        patient_summary=req.patient_summary,
        cancer_type=req.cancer_type,
        created_by=uid(user),
        status="open",
    )
    db.add(case)
    await db.commit()
    await db.refresh(case)
    return {"status": "created", "case": _to_case_json(case)}


@router.get("/cases/{cid}", summary="病例详情")
async def get_case(
    cid: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        raise HTTPException(404, "病例不存在")

    q = (
        select(MDTCase)
        .where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
        .options(selectinload(MDTCase.decisions))
    )
    case = (await db.execute(q)).scalar_one_or_none()
    if not case:
        raise HTTPException(404, "病例不存在")

    return _to_case_json(case, decisions=case.decisions)


@router.post("/cases/{cid}/decide", summary="MDT 决策")
async def add_decision(
    cid: uuid.UUID,
    req: MDTDecisionCreate,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        return {"status": "skipped", "note": "请先创建或加入团队"}

    case_q = select(MDTCase).where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
    case = (await db.execute(case_q)).scalar_one_or_none()
    if not case:
        raise HTTPException(404, "病例不存在")

    meeting_dt = None
    if req.meeting_date:
        try:
            meeting_dt = datetime.strptime(req.meeting_date, "%Y-%m-%d").replace(tzinfo=UTC)
        except ValueError:
            raise HTTPException(422, "日期格式错误，请使用 YYYY-MM-DD")

    decision = MDTDecision(
        case_id=cid,
        conclusion=req.conclusion,
        meeting_date=meeting_dt,
        participants=req.participants or [],
        decided_by=uid(user),
    )
    db.add(decision)

    # Update case status
    case.status = "decided"
    await db.commit()
    await db.refresh(decision)

    return {"status": "decided", "decision": _to_decision_json(decision)}


@router.get("/cases/{cid}/evidence", summary="病例证据列表")
async def list_evidence(
    cid: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        return {"items": [], "total": 0}

    # Verify case belongs to tenant
    case_q = select(MDTCase.id).where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
    exists = (await db.execute(case_q)).scalar_one_or_none()
    if not exists:
        raise HTTPException(404, "病例不存在")

    q = (
        select(MDTEvidence)
        .where(MDTEvidence.case_id == cid)
        .order_by(MDTEvidence.created_at.desc())
    )
    rows = (await db.execute(q)).scalars().all()
    return {"items": [_to_evidence_json(e) for e in rows], "total": len(rows)}


@router.post("/cases/{cid}/evidence", summary="添加文献证据")
async def add_evidence(
    cid: uuid.UUID,
    req: AddLiteratureReq,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        return {"status": "skipped", "note": "请先创建或加入团队"}

    case_q = select(MDTCase).where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
    case = (await db.execute(case_q)).scalar_one_or_none()
    if not case:
        raise HTTPException(404, "病例不存在")

    # Auto-fetch literature info from global_literature
    lit_q = select(GlobalLiterature).where(GlobalLiterature.pmid == req.pmid)
    lit = (await db.execute(lit_q)).scalar_one_or_none()
    if not lit:
        raise HTTPException(404, "文献不存在")
    citation = lit.title
    lit_id = lit.id

    evidence = MDTEvidence(
        case_id=cid,
        literature_id=lit_id,
        pmid=req.pmid,
        citation=citation,
        relevance_note=req.relevance_note,
        added_by=uid(user),
    )
    db.add(evidence)
    await db.commit()
    await db.refresh(evidence)

    return {"status": "added", "evidence": _to_evidence_json(evidence)}


@router.delete("/cases/{cid}/evidence/{eid}", summary="删除文献证据")
async def delete_evidence(
    cid: uuid.UUID,
    eid: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    tid = tenant_ctx.get()
    if not tid:
        return {"status": "skipped", "note": "请先创建或加入团队"}

    case_q = select(MDTCase.id).where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
    exists = (await db.execute(case_q)).scalar_one_or_none()
    if not exists:
        raise HTTPException(404, "病例不存在")

    q = select(MDTEvidence).where(MDTEvidence.id == eid, MDTEvidence.case_id == cid)
    ev = (await db.execute(q)).scalar_one_or_none()
    if not ev:
        raise HTTPException(404, "证据不存在")

    await db.delete(ev)
    await db.commit()
    return {"status": "deleted"}


@router.get("/cases/{cid}/literature-pack", summary="病例文献包")
async def literature_pack(
    cid: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    user: dict = Depends(get_current_user),
):
    """根据病例的癌种和摘要关键词，自动推荐相关的最新文献"""
    tid = tenant_ctx.get()
    if not tid:
        return {"items": [], "total": 0, "terms": []}

    # Get case
    case_q = select(MDTCase).where(MDTCase.id == cid, MDTCase.tenant_id == uuid.UUID(tid))
    case = (await db.execute(case_q)).scalar_one_or_none()
    if not case:
        raise HTTPException(404, "病例不存在")

    # Build search terms from cancer_type + patient_summary keywords
    terms: list[str] = []
    if case.cancer_type:
        for t in re.split(r'[/、,，\s]+', case.cancer_type):
            t = t.strip()
            if t and len(t) > 1:
                terms.append(t)

    # Extract key clinical terms from patient summary
    summary_clean = re.sub(r'[^一-鿿\w\s]', ' ', case.patient_summary or '')
    words = [w.strip() for w in summary_clean.split() if len(w.strip()) > 1]
    for w in words[:6]:
        if w not in terms:
            terms.append(w)

    if not terms:
        terms = ["cancer", "tumor", "肿瘤"]

    # Search by tags matching cancer_type
    tag_lit_ids: set[uuid.UUID] = set()
    if case.cancer_type:
        ct = case.cancer_type.replace('癌', '').replace('瘤', '').strip()
        tag_conds = [
            GlobalTag.name_zh.ilike(f"%{case.cancer_type}%"),
            GlobalTag.path.ilike(f"%{case.cancer_type}%"),
        ]
        if ct and ct != case.cancer_type:
            tag_conds.append(GlobalTag.name_en.ilike(f"%{ct}%"))

        tag_result = await db.execute(
            select(GlobalLiteratureTag.literature_id)
            .join(GlobalTag)
            .where(or_(*tag_conds))
            .limit(50)
        )
        tag_lit_ids = set(tag_result.scalars().all())

    # Get already-added PMIDs to exclude
    existing_pmids_q = select(MDTEvidence.pmid).where(
        MDTEvidence.case_id == cid,
        MDTEvidence.pmid.isnot(None),
    )
    existing: set[int] = set()
    for (pmid,) in (await db.execute(existing_pmids_q)):
        if pmid is not None:
            existing.add(pmid)

    # Query literature: search in title/abstract + prefer tag-matched + highly cited + recent
    search_conds = [GlobalLiterature.pmid.notin_(existing)] if existing else [true()]
    text_conds = []
    for t in terms:
        like = f"%{t}%"
        text_conds.append(GlobalLiterature.title.ilike(like))
        text_conds.append(GlobalLiterature.abstract.ilike(like))
    search_conds.append(or_(*text_conds))

    rows = (await db.execute(
        select(GlobalLiterature)
        .where(*search_conds)
        .order_by(GlobalLiterature.cited_by_count.desc().nullslast(),
                  GlobalLiterature.pub_date.desc().nullslast())
        .limit(20)
    )).scalars().all()

    # Score and rank
    suggestions = []
    for lit in rows:
        score = 0
        reason_parts = []

        # Tag match
        if tag_lit_ids and lit.id in tag_lit_ids:
            score += 10
            reason_parts.append("标签匹配")

        # Count term matches
        title_lower = (lit.title or "").lower()
        abstract_lower = (lit.abstract or "").lower()
        matched = []
        for t in terms:
            tl = t.lower()
            if tl in title_lower:
                score += 3
                matched.append(t)
            elif tl in abstract_lower:
                score += 1
                if t not in matched:
                    matched.append(t)
        if matched:
            reason_parts.append(f"关键词: {'/'.join(matched[:3])}")

        # Citation boost
        cited = lit.cited_by_count or 0
        if cited > 50:
            score += 5
        elif cited > 10:
            score += 3
        elif cited > 0:
            score += 1

        # Recency boost
        if lit.pub_date:
            days_old = (datetime.now(UTC).date() - lit.pub_date).days
            if days_old < 90:
                score += 5
            elif days_old < 365:
                score += 2

        if score > 0:
            tag_names: list[str] = []
            if tag_lit_ids and lit.id in tag_lit_ids:
                try:
                    tr = await db.execute(
                        select(GlobalTag.name_zh)
                        .join(GlobalLiteratureTag)
                        .where(GlobalLiteratureTag.literature_id == lit.id)
                        .limit(3)
                    )
                    tag_names = [r for (r,) in tr]
                except Exception:
                    pass

            authors = lit.authors or []
            suggestions.append({
                "pmid": lit.pmid,
                "title": lit.title,
                "journal": lit.journal,
                "pub_date": cap_pub_date(lit.pub_date),
                "cited_by_count": cited,
                "first_author": authors[0].get("family", "") if authors else "",
                "reason": "、".join(reason_parts),
                "relevance_score": score,
                "tags": tag_names,
            })

    suggestions.sort(key=lambda x: x["relevance_score"], reverse=True)
    return {"items": suggestions[:10], "total": len(suggestions), "terms": terms}
