"""Drug↔Cancer Type 匹配服务

集中管理药品到癌种标签的映射逻辑：
- FDA 同步管线创建新审批后调用
- 适应症解析管线解析后回填
- 管理后台批处理回填
"""

import logging
from collections import defaultdict

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.business import DrugApproval, DrugLabel
from app.models.literature import GlobalTag

logger = logging.getLogger(__name__)

# ── 靶点 → 癌种映射（保守，仅明确一对一的） ──
TARGET_CANCER_MAP: dict[str, str] = {
    "EGFR": "Lung Cancer",
    "ALK": "Lung Cancer",
    "ROS1": "Lung Cancer",
    "MET": "Lung Cancer",
    "EGFR/MET": "Lung Cancer",
    "ALK/ROS1": "Lung Cancer",
    "FLT3": "Leukemia",
    "BTK": "Lymphoma",
    "BCR-ABL": "Leukemia",
    "BCMA": "Multiple Myeloma",
    "BCMA/CD3": "Multiple Myeloma",
    "GPRC5D/CD3": "Multiple Myeloma",
    "CD38": "Multiple Myeloma",
    "CD20": "Lymphoma",
    "CD30": "Lymphoma",
    "AR": "Prostate Cancer",
    "CYP17": "Prostate Cancer",
    "PDGFRA": "GIST",
    "KIT": "GIST",
    "NTRK": "NTRK Fusion Cancer",
    "NTRK/ROS1": "NTRK Fusion Cancer",
    "CCR4": "Lymphoma",
}

# ── CANCER_TAG_MAP（关键词 → 标准癌种名_en） ──
# 与 backfill_drug_tags.py 保持同步，合并了两个文件中的条目
CANCER_TAG_MAP: dict[str, str] = {
    "lung cancer": "Lung Cancer",
    "lung carcinoma": "Lung Cancer",
    "nsclc": "Lung Cancer",
    "non-small cell lung": "Lung Cancer",
    "small cell lung": "Lung Cancer",
    "sclc": "Lung Cancer",
    "breast cancer": "Breast Cancer",
    "breast carcinoma": "Breast Cancer",
    "colorectal": "Colorectal Cancer",
    "colon cancer": "Colorectal Cancer",
    "colon carcinoma": "Colorectal Cancer",
    "rectal cancer": "Colorectal Cancer",
    "rectal carcinoma": "Colorectal Cancer",
    "gastric": "Gastric Cancer",
    "gastroesophageal": "Gastric Cancer",
    "stomach": "Gastric Cancer",
    "gastric carcinoma": "Gastric Cancer",
    "pancreatic": "Pancreatic Cancer",
    "pancreatic carcinoma": "Pancreatic Cancer",
    "prostate": "Prostate Cancer",
    "prostatic": "Prostate Cancer",
    "prostate carcinoma": "Prostate Cancer",
    "hepatocellular": "Liver Cancer",
    "hcc": "Liver Cancer",
    "liver cancer": "Liver Cancer",
    "liver carcinoma": "Liver Cancer",
    "melanoma": "Melanoma",
    "ovarian": "Ovarian Cancer",
    "ovarian carcinoma": "Ovarian Cancer",
    "cervical": "Cervical Cancer",
    "cervical carcinoma": "Cervical Cancer",
    "head and neck": "Head and Neck Cancer",
    "hnscc": "Head and Neck Cancer",
    "esophageal": "Esophageal Cancer",
    "esophagus": "Esophageal Cancer",
    "esophageal carcinoma": "Esophageal Cancer",
    "renal": "Kidney Cancer",
    "rcc": "Kidney Cancer",
    "kidney cancer": "Kidney Cancer",
    "kidney carcinoma": "Kidney Cancer",
    "bladder": "Bladder Cancer",
    "bladder carcinoma": "Bladder Cancer",
    "urothelial": "Bladder Cancer",
    "lymphoma": "Lymphoma",
    "hodgkin": "Lymphoma",
    "leukemia": "Leukemia",
    "all": "Leukemia",
    "aml": "Leukemia",
    "cll": "Leukemia",
    "cml": "Leukemia",
    "multiple myeloma": "Multiple Myeloma",
    "myeloma": "Multiple Myeloma",
    "glioblastoma": "Brain Cancer",
    "gbm": "Brain Cancer",
    "glioma": "Brain Cancer",
    "thyroid": "Thyroid Cancer",
    "thyroid carcinoma": "Thyroid Cancer",
    "mesothelioma": "Mesothelioma",
    "cholangiocarcinoma": "Cholangiocarcinoma",
    "bile duct": "Cholangiocarcinoma",
    "gist": "GIST",
    "sarcoma": "Sarcoma",
    "endometrial": "Endometrial Cancer",
    "endometrial carcinoma": "Endometrial Cancer",
    "uterine": "Endometrial Cancer",
    "merkel cell": "Merkel Cell Carcinoma",
    "msi-h": "Colorectal Cancer",
    "dmmr": "Colorectal Cancer",
    "ntrk": "NTRK Fusion Cancer",
    "nasopharyngeal": "Nasopharyngeal Carcinoma",
    "breast carcinoma": "Breast Cancer",
}

# ── 已知药品 → 癌种直接映射（通用名→标准癌种名_en） ──
DRUG_CANCER_MAP: dict[str, str] = {
    # 肺癌
    "osimertinib": "Lung Cancer",
    "gefitinib": "Lung Cancer",
    "erlotinib": "Lung Cancer",
    "afatinib": "Lung Cancer",
    "dacomitinib": "Lung Cancer",
    "icotinib": "Lung Cancer",
    "crizotinib": "Lung Cancer",
    "alectinib": "Lung Cancer",
    "brigatinib": "Lung Cancer",
    "lorlatinib": "Lung Cancer",
    "ceritinib": "Lung Cancer",
    "capmatinib": "Lung Cancer",
    "tepotinib": "Lung Cancer",
    "amivantamab": "Lung Cancer",
    "lazertinib": "Lung Cancer",
    "selpercatinib": "Lung Cancer",  # primarily lung, also thyroid
    "pralsetinib": "Lung Cancer",
    # 乳腺癌
    "palbociclib": "Breast Cancer",
    "ribociclib": "Breast Cancer",
    "abemaciclib": "Breast Cancer",
    "lapatinib": "Breast Cancer",
    "neratinib": "Breast Cancer",
    "tucatinib": "Breast Cancer",
    "zongertinib": "Breast Cancer",
    # 白血病
    "midostaurin": "Leukemia",
    "gilteritinib": "Leukemia",
    "ivosidenib": "Leukemia",
    "enasidenib": "Leukemia",
    "imatinib": "Leukemia",
    "dasatinib": "Leukemia",
    "nilotinib": "Leukemia",
    "bosutinib": "Leukemia",
    "ponatinib": "Leukemia",
    # 淋巴瘤
    "ibrutinib": "Lymphoma",
    "acalabrutinib": "Lymphoma",
    "zanubrutinib": "Lymphoma",
    "pirtobrutinib": "Lymphoma",
    "rilzabrutinib": "Lymphoma",
    "idelalisib": "Lymphoma",
    "copanlisib": "Lymphoma",
    # 黑色素瘤
    "vemurafenib": "Melanoma",
    "dabrafenib": "Melanoma",
    "encorafenib": "Melanoma",
    "trametinib": "Melanoma",
    "cobimetinib": "Melanoma",
    "binimetinib": "Melanoma",
    # 前列腺癌
    "enzalutamide": "Prostate Cancer",
    "apalutamide": "Prostate Cancer",
    "darolutamide": "Prostate Cancer",
    "abiraterone": "Prostate Cancer",
    # 多发性骨髓瘤
    "daratumumab": "Multiple Myeloma",
    "isatuximab": "Multiple Myeloma",
    "carfilzomib": "Multiple Myeloma",
    "ixazomib": "Multiple Myeloma",
    "pomalidomide": "Multiple Myeloma",
    "teclistamab": "Multiple Myeloma",
    "talquetamab": "Multiple Myeloma",
    "idecabtagene vicleucel": "Multiple Myeloma",
}

# ── 已知品牌名 → 癌种直接映射 ──
BRAND_CANCER_MAP: dict[str, str] = {
    "rydapt": "Leukemia",
}

# ── 从适应症文本中提取癌种关键词（合并自 approvals.py + backfill_drug_tags.py） ──
CANCER_KEYWORD_SET: list[str] = [
    "NSCLC", "non-small cell lung", "small cell lung", "SCLC",
    "breast cancer", "breast carcinoma",
    "colorectal", "colon cancer", "colon carcinoma",
    "rectal cancer", "rectal carcinoma",
    "gastric", "gastroesophageal", "stomach cancer",
    "gastric carcinoma",
    "pancreatic cancer", "pancreatic adenocarcinoma",
    "pancreatic carcinoma",
    "prostate cancer", "prostatic",
    "prostate carcinoma",
    "hepatocellular", "HCC", "liver cancer", "hepatic",
    "liver carcinoma",
    "melanoma", "malignant melanoma",
    "ovarian cancer", "ovarian carcinoma",
    "cervical cancer", "cervical carcinoma",
    "head and neck", "HNSCC",
    "esophageal", "esophagus",
    "esophageal carcinoma",
    "renal cell", "RCC", "kidney cancer",
    "kidney carcinoma",
    "bladder cancer", "bladder carcinoma",
    "urothelial",
    "lymphoma", "Hodgkin", "non-Hodgkin",
    "leukemia", "ALL", "AML", "CLL", "CML",
    "multiple myeloma", "myeloma",
    "glioblastoma", "GBM", "glioma",
    "thyroid cancer", "thyroid carcinoma",
    "mesothelioma",
    "cholangiocarcinoma", "bile duct",
    "GIST", "gastrointestinal stromal",
    "sarcoma", "soft tissue",
    "endometrial", "endometrial carcinoma", "uterine",
    "Merkel cell",
    "MSI-H", "dMMR", "microsatellite instability",
    "NTRK", "TRK fusion",
    "nasopharyngeal",
]


def extract_cancers_from_text(text: str) -> list[str]:
    """从任意文本中提取癌种关键词"""
    if not text:
        return []
    lower = text.lower()
    found: set[str] = set()
    for kw in CANCER_KEYWORD_SET:
        if kw.lower() in lower:
            found.add(kw)
    return sorted(found)


def _find_tag_by_name(tag_cache: dict[str, str], standard_name: str) -> str | None:
    """在标签缓存中查找标准癌种名对应的 ID"""
    return tag_cache.get(standard_name.lower().strip())


def _match_cancer_from_text(text: str) -> str | None:
    """从文本中匹配癌种关键词，返回标准癌种名或 None"""
    if not text:
        return None
    lower = text.lower()
    for kw, std_name in CANCER_TAG_MAP.items():
        if kw in lower:
            return std_name
    return None


async def _build_tag_cache(db: AsyncSession) -> dict[str, str]:
    """构建癌种标签 name_en/name_zh → id 缓存"""
    r = await db.execute(
        select(GlobalTag).where(
            GlobalTag.tag_category == "cancer",
            GlobalTag.is_active == True,
        )
    )
    cache: dict[str, str] = {}
    for t in r.scalars():
        if t.name_en:
            cache[t.name_en.lower().strip()] = str(t.id)
        if t.name_zh:
            cache[t.name_zh.lower().strip()] = str(t.id)
    return cache


async def resolve_cancer_type_tag(
    db: AsyncSession,
    generic_name: str,
    drug_name: str,
    target: str | None,
    *,
    tag_cache: dict[str, str] | None = None,
) -> str | None:
    """三层匹配：parsed_indications → 药品名 → 靶点，返回 GlobalTag.id 或 None"""
    if tag_cache is None:
        tag_cache = await _build_tag_cache(db)

    # Tier 1: 已有 DrugLabel.parsed_indications
    if generic_name:
        r = await db.execute(
            select(DrugLabel.parsed_indications)
            .where(
                DrugLabel.generic_name == generic_name,
                DrugLabel.parsed_indications.isnot(None),
            )
            .limit(5)
        )
        for row in r:
            parsed = row[0] or {}
            for c in parsed.get("cancers", []):
                ct = c.get("cancer_type")
                if ct:
                    tid = tag_cache.get(ct.lower().strip())
                    if tid:
                        return tid

    # Tier 2: 已知药品名 → 癌种
    for name in [generic_name, drug_name]:
        if not name:
            continue
        lower = name.lower().strip()
        std_name = BRAND_CANCER_MAP.get(lower) or DRUG_CANCER_MAP.get(lower)
        if std_name:
            tid = tag_cache.get(std_name.lower())
            if tid:
                return tid

    # Tier 3: 靶点名 → 癌种
    if target:
        norm_target = target.upper()
        # 直接匹配
        std_name = TARGET_CANCER_MAP.get(norm_target)
        if std_name:
            tid = tag_cache.get(std_name.lower())
            if tid:
                return tid
        # 子串匹配（如 "VEGFR" 含 "EGFR"）
        for key, val in TARGET_CANCER_MAP.items():
            if key in norm_target or norm_target in key:
                tid = tag_cache.get(val.lower())
                if tid:
                    return tid
        # 从目标名中提取癌种关键词
        std_name = _match_cancer_from_text(norm_target)
        if std_name:
            tid = tag_cache.get(std_name.lower())
            if tid:
                return tid

    return None


async def backfill_from_labels(db: AsyncSession) -> int:
    """从 DrugLabel.parsed_indications 回填 DrugApproval.cancer_type_tag_id

    用于：
    - parse-indications 管线完成后的回填
    - 管理后台手动触发的批处理
    """
    tag_cache = await _build_tag_cache(db)

    # 收集有 parsed_indications 的药品
    r = await db.execute(
        select(DrugLabel).where(DrugLabel.parsed_indications.isnot(None))
    )
    label_cancers: dict[str, set[str]] = defaultdict(set)
    for lb in r.scalars():
        parsed = lb.parsed_indications or {}
        for c in parsed.get("cancers", []):
            ct = c.get("cancer_type")
            if ct:
                if lb.generic_name:
                    label_cancers[lb.generic_name.lower().strip()].add(ct)
                if lb.drug_name:
                    label_cancers[lb.drug_name.lower().strip()].add(ct)

    # 找所有缺失 tag_id 的审批
    r = await db.execute(
        select(DrugApproval).where(DrugApproval.cancer_type_tag_id.is_(None))
    )
    missing = r.scalars().all()
    fixed = 0
    for d in missing:
        for name_key in [d.generic_name.lower().strip(), d.drug_name.lower().strip()]:
            if name_key in label_cancers:
                for ct in label_cancers[name_key]:
                    tid = tag_cache.get(ct.lower().strip())
                    if tid:
                        d.cancer_type_tag_id = tid
                        fixed += 1
                        break
                if d.cancer_type_tag_id:
                    break

    if fixed:
        await db.commit()
        logger.info("Drug tag backfill: %d records updated from parsed_indications", fixed)
    return fixed


async def backfill_from_maps(db: AsyncSession) -> int:
    """用药品名+靶点映射批量回填缺失的 cancer_type_tag_id
    独立于 parsed_indications（因为有些药没有 label，但已知靶点对应癌种）
    """
    tag_cache = await _build_tag_cache(db)
    r = await db.execute(
        select(DrugApproval).where(DrugApproval.cancer_type_tag_id.is_(None))
    )
    missing = r.scalars().all()
    if not missing:
        return 0

    fixed = 0
    for d in missing:
        tid = await resolve_cancer_type_tag(
            db, d.generic_name, d.drug_name, d.target,
            tag_cache=tag_cache,
        )
        if tid:
            d.cancer_type_tag_id = tid
            fixed += 1

    if fixed:
        await db.commit()
        logger.info("Drug tag backfill (maps): %d records updated", fixed)
    return fixed
