"""FDA 新药审批数据同步服务

数据流：
  1. 用 EPC 分类 + 命名约定搜索抗肿瘤药记录
  2. 去重后解析 ORIG / SUPPL EFFICACY 提交
  3. Upsert drug_approvals 表
"""

import logging
from datetime import date, timedelta

import httpx
from sqlalchemy import select

from app.db import async_session
from app.models.business import DrugApproval
from app.services.fda_api import (
    extract_efficacy_supplements,
    extract_or_orig_submission,
    is_candidate_oncology_drug,
    parse_fda_date,
    search_oncology_drugs,
    search_recent_nda_approvals,
)

logger = logging.getLogger(__name__)

# 已知抗肿瘤药通用名 → 靶点映射（覆盖 EPC 判断不全的药物）
TARGET_MAP = {
    "osimertinib": "EGFR", "gefitinib": "EGFR", "erlotinib": "EGFR",
    "afatinib": "EGFR", "dacomitinib": "EGFR", "icotinib": "EGFR",
    "cetuximab": "EGFR", "panitumumab": "EGFR", "necitu": "EGFR",
    "trastuzumab": "HER2", "pertuzumab": "HER2", "t-dxd": "HER2",
    "lapatinib": "HER2", "neratinib": "HER2", "tucatinib": "HER2",
    "pembrolizumab": "PD-1", "nivolumab": "PD-1", "cemiplimab": "PD-1",
    "dostarlimab": "PD-1", "toripalimab": "PD-1", "tislelizumab": "PD-1",
    "sintilimab": "PD-1", "cindilimab": "PD-1",
    "durvalumab": "PD-L1", "atezolizumab": "PD-L1", "avelumab": "PD-L1",
    "ipilimumab": "CTLA-4", "tremelimumab": "CTLA-4",
    "bevacizumab": "VEGF", "ramucirumab": "VEGFR2",
    "sorafenib": "VEGFR", "sunitinib": "VEGFR", "pazopanib": "VEGFR",
    "axitinib": "VEGFR", "lenvatinib": "VEGFR", "cabozantinib": "VEGFR/MET",
    "crizotinib": "ALK/ROS1", "alectinib": "ALK", "brigatinib": "ALK",
    "lorlatinib": "ALK/ROS1", "ceritinib": "ALK",
    "osimertinib": "EGFR", "alectinib": "ALK",
    "vemurafenib": "BRAF", "dabrafenib": "BRAF", "encorafenib": "BRAF",
    "trametinib": "MEK", "cobimetinib": "MEK", "binimetinib": "MEK",
    "imatinib": "BCR-ABL", "dasatinib": "BCR-ABL", "nilotinib": "BCR-ABL",
    "bosutinib": "BCR-ABL", "ponatinib": "BCR-ABL",
    "ibrutinib": "BTK", "acalabrutinib": "BTK", "zanubrutinib": "BTK",
    "palbociclib": "CDK4/6", "ribociclib": "CDK4/6", "abemaciclib": "CDK4/6",
    "olaparib": "PARP", "niraparib": "PARP", "rucaparib": "PARP",
    "talazoparib": "PARP", "fluzoparib": "PARP", "pamiparib": "PARP",
    "everolimus": "mTOR", "temsirolimus": "mTOR",
    "bortezomib": "Proteasome", "carfilzomib": "Proteasome",
    "ixazomib": "Proteasome",
    "lenalidomide": "Immunomodulatory", "pomalidomide": "Immunomodulatory",
    "thalidomide": "Immunomodulatory",
    "enzalutamide": "AR", "apalutamide": "AR", "darolutamide": "AR",
    "abiraterone": "CYP17",
    "letrozole": "Aromatase", "anastrozole": "Aromatase",
    "exemestane": "Aromatase",
    "tamoxifen": "ER", "fulvestrant": "ER",
    "rituximab": "CD20", "obinutuzumab": "CD20", "ofatumumab": "CD20",
    "daratumumab": "CD38", "isatuximab": "CD38",
    "brentuximab": "CD30",
    "blinatumomab": "CD19/CD3",
    "polatuzumab": "CD79b",
    "sacituzumab": "TROP2",
    "enfortumab": "NECTIN4",
    "belantamab": "BCMA",
    "teclistamab": "BCMA/CD3",
    "talquetamab": "GPRC5D/CD3",
    "idecabtagene": "BCMA",
    "tisagenlecleucel": "CD19",
    "axicabtagene": "CD19",
    "brexucabtagene": "CD19",
    "lisocabtagene": "CD19",
    "delgocitinib": "JAK",
    "repotrectinib": "NTRK/ROS1",
    "avapritinib": "PDGFRA",
    "vandetanib": "RET",
    "mirdametinib": "MEK",
    "zongertinib": "HER2",
    "inavolisib": "PI3K",
    "ruxolitinib": "JAK",
    "pirtobrutinib": "BTK",
    "selumetinib": "MEK",
    "lazertinib": "EGFR",
    "futibatinib": "FGFR",
    "tovorafenib": "BRAF",
    "mogamulizumab": "CCR4",
    "ripretinib": "KIT",
    "vimseltinib": "CSF1R",
    "pacritinib": "JAK",
    "rilzabrutinib": "BTK",
    "idelalisib": "PI3K",
    "rydapt": "FLT3",  # midostaurin（通用名字段存了品牌名）
    "nintedanib": "FGFR/VEGFR",
    "erdafitinib": "FGFR",
    "pemigatinib": "FGFR",
    "infigatinib": "FGFR",
    "larotrectinib": "NTRK", "entrectinib": "NTRK/ROS1",
    "selpercatinib": "RET", "pralsetinib": "RET",
    "capmatinib": "MET", "tepotinib": "MET",
    "amivantamab": "EGFR/MET",
    "midostaurin": "FLT3", "gilteritinib": "FLT3",
    "ivosidenib": "IDH1", "enasidenib": "IDH2",
    "belinostat": "HDAC", "vorinostat": "HDAC", "romidepsin": "HDAC",
    "panobinostat": "HDAC",
    "bexarotene": "RXR",
    "arsenic trioxide": "APL",
    "tretinoin": "APL",
    "vingristine": "Microtubule",
    "paclitaxel": "Microtubule", "nab-paclitaxel": "Microtubule",
    "docetaxel": "Microtubule",
    "etoposide": "Topoisomerase II",
    "irinotecan": "Topoisomerase I",
    "doxorubicin": "Topoisomerase II",
    "daunorubicin": "Topoisomerase II",
    "cytarabine": "Antimetabolite",
    "gemcitabine": "Antimetabolite",
    "fluorouracil": "Antimetabolite",
    "capecitabine": "Antimetabolite",
    "methotrexate": "Antimetabolite",
    "pemetrexed": "Antimetabolite",
    "pralatrexate": "Antimetabolite",
    "fludarabine": "Antimetabolite",
    "cladribine": "Antimetabolite",
    "bendamustine": "Alkylating",
    "cyclophosphamide": "Alkylating",
    "ifosfamide": "Alkylating",
    "temozolomide": "Alkylating",
    "carboplatin": "Platinum", "cisplatin": "Platinum",
    "oxaliplatin": "Platinum",
    "lurbinectedin": "DNA-binding",
}


async def sync_all_fda_approvals() -> dict:
    """全量同步 FDA 抗肿瘤药审批数据"""
    stats = {"records_found": 0, "oncology_confirmed": 0, "new_records": 0, "errors": 0}

    async with httpx.AsyncClient() as client:
        # Phase 1: 通过 EPC 分类搜索抗肿瘤药记录
        logger.info("Phase 1: Searching oncology drugs via EPC/pharm_class...")
        details = await search_oncology_drugs(client)
        stats["records_found"] = len(details)
        if not details:
            logger.warning("No oncology drug records found")
            stats["status"] = "ok"
            return stats

        # 预构建 tag_cache（只需一次）
        from app.db import async_session
        from app.services.drug_cancer_mapper import resolve_cancer_type_tag
        async with async_session() as _init_db:
            from app.services.drug_cancer_mapper import _build_tag_cache
            tag_cache = await _build_tag_cache(_init_db)

        # Phase 2: 处理每条记录 → upsert
        logger.info("Phase 2: Processing %d records...", len(details))
        for detail in details:
            try:
                ofda = detail.get("openfda", {})
                brand = _first(ofda, "brand_name")
                generic = _first(ofda, "generic_name") or _first(ofda, "substance_name")
                epc_list = ofda.get("pharm_class_epc", [])

                # 二次确认
                if not is_candidate_oncology_drug(brand, generic, epc_list):
                    continue

                stats["oncology_confirmed"] += 1
                await _process_approval(detail, brand, generic, epc_list, tag_cache=tag_cache)
                stats["new_records"] += 1

            except Exception:
                logger.exception("Error processing %s", detail.get("application_number", ""))
                stats["errors"] += 1

        # Phase 3: 额外查最近 90 天新批药
        try:
            recent = await _sync_recent(client)
            stats["recent_new"] = recent
        except Exception:
            logger.exception("Error syncing recent")

    stats["status"] = "ok"
    logger.info(
        "FDA sync complete: %d records, %d oncology confirmed, %d upserted",
        stats["records_found"], stats["oncology_confirmed"], stats["new_records"],
    )

    # 通知关注癌种的用户
    await _notify_new_approvals()
    return stats


async def sync_recent_fda_approvals() -> dict:
    """同步最近 90 天的新批肿瘤药"""
    async with httpx.AsyncClient() as client:
        count = await _sync_recent(client)
    return {"status": "ok", "new_records": count}


async def _sync_recent(client: httpx.AsyncClient) -> int:
    """内部：扫最近 90 天 ORIG 提交"""
    since = date.today() - timedelta(days=90)
    count = 0
    skip = 0
    total = None

    # 预构建 tag_cache
    from app.db import async_session
    from app.services.drug_cancer_mapper import _build_tag_cache
    async with async_session() as _init_db:
        tag_cache = await _build_tag_cache(_init_db)

    while total is None or skip < total:
        data = await search_recent_nda_approvals(client, since, skip=skip, limit=100)
        if not data or not data.get("results"):
            break
        if total is None:
            total = data["meta"]["results"]["total"]
            if total == 0:
                return 0
            logger.info("Found %d recent ORIG submissions", total)

        for result in data["results"]:
            ofda = result.get("openfda", {})
            brand = _first(ofda, "brand_name")
            generic = _first(ofda, "generic_name") or _first(ofda, "substance_name")
            epc = ofda.get("pharm_class_epc", [])
            if is_candidate_oncology_drug(brand, generic, epc):
                await _process_approval(result, brand, generic, epc, tag_cache=tag_cache)
                count += 1

        skip += 100

    if count:
        logger.info("Recent sync: %d new oncology approvals", count)
        await _notify_new_approvals()
    return count


# ─── 内部处理 ───


def _first(ofda: dict, field: str, default: str = "") -> str:
    vals = ofda.get(field, [])
    return str(vals[0]) if isinstance(vals, list) and vals else default


def _lookup_target(generic_name: str) -> str | None:
    """从通用名查找靶点"""
    name_lower = generic_name.lower().strip()
    # 精确匹配优先
    if name_lower in TARGET_MAP:
        return TARGET_MAP[name_lower]
    # 子串匹配
    for key, val in TARGET_MAP.items():
        if key in name_lower:
            return val
    return None


def _extract_target(epc_list: list[str], generic_name: str, brand_name: str) -> str | None:
    """提取靶点：EPC → 通用名查表 → 品牌名查表"""
    # 1. EPC 提取
    for epc in epc_list:
        t = epc.lower()
        # "Programmed Death" → PD-1 / PD-L1
        if "programmed death" in t:
            if "ligand" in t:
                return "PD-L1"
            return "PD-1"
        for kw in ["egfr", "vegfr", "her2", "pd-1", "pd-l1", "ctla-4",
                     "cdk", "parp", "btk", "mek", "braf", "alk",
                     "mtor", "pi3k", "flt3", "idh", "bcr", "abl",
                     "ret", "ntrk", "ros1", "met", "fgfr"]:
            if kw in t:
                return kw.upper()
    # 2. 通用名查表
    target = _lookup_target(generic_name)
    if target:
        return target
    # 3. 品牌名查表
    target = _lookup_target(brand_name)
    if target:
        return target
    return None


async def _process_approval(
    detail: dict, brand: str, generic: str, epc_list: list[str],
    *,
    tag_cache: dict[str, str] | None = None,
):
    """处理单支药的审批详情，写入 drug_approvals"""
    app_number = detail.get("application_number", "")
    submissions = detail.get("submissions", [])
    sponsor = detail.get("sponsor_name", "")

    source_url = (
        f"https://www.accessdata.fda.gov/scripts/cder/daf/index.cfm?"
        f"event=overview.process&ApplNo={app_number.replace('NDA','').replace('BLA','')}"
    )

    target = _extract_target(epc_list, generic, brand)
    drug_name = brand or generic or app_number

    # 在同步阶段尝试匹配 cancer_type_tag_id
    from app.services.drug_cancer_mapper import resolve_cancer_type_tag
    # 如果这个药已经有 DrugLabel，用 parsed_indications 匹配
    # 否则用 target/药品名映射
    async with async_session() as _map_db:
        resolved_tag_id = await resolve_cancer_type_tag(
            _map_db, generic, drug_name, target,
            tag_cache=tag_cache,
        )

    # ORIG → new_drug
    orig = extract_or_orig_submission(submissions)
    if orig:
        d = parse_fda_date(orig.get("submission_status_date"))
        if d:
            await _upsert(
                drug_name=drug_name[:500],
                generic_name=generic[:500],
                target=target,
                indication=_build_indication_text(drug_name, "new_drug", target, generic),
                approval_agency="FDA",
                approval_type="new_drug",
                approval_date=d,
                source_url=source_url,
                cancer_type_tag_id=resolved_tag_id,
            )

    # SUPPL EFFICACY → expanded_indication
    for suppl in extract_efficacy_supplements(submissions):
        d = parse_fda_date(suppl.get("submission_status_date"))
        if d:
            await _upsert(
                drug_name=drug_name[:500],
                generic_name=generic[:500],
                target=target,
                indication=_build_indication_text(drug_name, "expanded_indication", target, generic),
                approval_agency="FDA",
                approval_type="expanded_indication",
                approval_date=d,
                source_url=source_url,
                cancer_type_tag_id=resolved_tag_id,
            )


def _build_indication_text(drug_name: str, approval_type: str, target: str | None, generic: str | None) -> str:
    """构建比纯占位符更好的 indication 文本"""
    if target:
        return f"{drug_name} ({target}) — { '初始 FDA 批准' if approval_type == 'new_drug' else 'FDA 适应症扩展'}"
    return f"{drug_name} — {'初始 FDA 批准' if approval_type == 'new_drug' else 'FDA 适应症扩展'}"


async def _upsert(
    drug_name: str, generic_name: str, target: str | None,
    indication: str, approval_agency: str, approval_type: str,
    approval_date: date, source_url: str | None,
    cancer_type_tag_id: str | None = None,
):
    """按 (drug_name, approval_date, type) 去重写入"""
    async with async_session() as db:
        try:
            existing = await db.execute(
                select(DrugApproval).where(
                    DrugApproval.drug_name == drug_name,
                    DrugApproval.approval_date == approval_date,
                    DrugApproval.approval_type == approval_type,
                )
            )
            if existing.scalar():
                return

            from app.db import new_uuid
            import uuid
            da = DrugApproval(
                id=new_uuid(),
                drug_name=drug_name,
                generic_name=generic_name,
                target=target[:200] if target else None,
                indication=indication,
                cancer_type_tag_id=uuid.UUID(cancer_type_tag_id) if cancer_type_tag_id else None,
                approval_agency=approval_agency,
                approval_type=approval_type,
                approval_date=approval_date,
                source_url=source_url[:1000] if source_url else None,
            )
            db.add(da)
            await db.commit()
            logger.debug("  + %s %s %s tag_id=%s", approval_date, drug_name, approval_type, cancer_type_tag_id[:8] if cancer_type_tag_id else "None")
        except Exception:
            await db.rollback()


async def _notify_new_approvals():
    """同步完成后通知关注对应癌种的用户"""
    from datetime import datetime, timezone, timedelta

    from app.services.cancer_notification import notify_new_drug_approval

    cutoff = datetime.now(timezone.utc) - timedelta(hours=2)
    # 查过去 2 小时内有 cancer_type_tag_id 的新审批
    async with async_session() as db:
        from sqlalchemy import func as _func
        r = await db.execute(
            select(DrugApproval)
            .where(
                DrugApproval.created_at >= cutoff,
                DrugApproval.cancer_type_tag_id.isnot(None),
            )
        )
        new_approvals = r.scalars().all()
        if not new_approvals:
            return

        notified_tag_ids: set[str] = set()
        for a in new_approvals:
            if a.cancer_type_tag_id and str(a.cancer_type_tag_id) not in notified_tag_ids:
                notified_tag_ids.add(str(a.cancer_type_tag_id))
                try:
                    await notify_new_drug_approval(
                        db, a.cancer_type_tag_id, a.drug_name,
                        a.target or "", a.indication or "",
                    )
                except Exception:
                    logger.exception("Notification failed for tag %s", a.cancer_type_tag_id)

        logger.info(
            "Sent notifications for %d cancer tags (%d new approvals)",
            len(notified_tag_ids), len(new_approvals),
        )
