"""FDA openFDA API 客户端：查询药品审批数据

数据源官方文档：https://open.fda.gov/apis/drug/drugsfda/

注意：本模块仅查询公开开放数据，不涉及任何受限制信息。
FDA 明确授权程序化访问（https://open.fda.gov/terms/）。
"""

import asyncio
import logging
import time
from datetime import date

import httpx

logger = logging.getLogger(__name__)

FDA_API_BASE = "https://api.fda.gov"

# 限速：无 API Key 约 240 req/min，保守用 3 req/s
# 用时间戳而非 asyncio.Lock（避免跨事件循环问题）
RATE_LIMIT_INTERVAL = 0.35
MAX_RETRIES = 3
_last_request_time = 0.0
_rate_lock = asyncio.Lock()  # 仅保护 _last_request_time 写入，不跨事件循环保持

# 抗肿瘤药相关的 EPC 分类关键词
ONCOLOGY_EPC_PATTERNS = [
    "kinase inhibitor", "parp inhibitor", "mtor inhibitor",
    "cdk inhibitor", "hdac inhibitor", "proteasome inhibitor",
    "topoisomerase inhibitor", "mitotic inhibitor",
    "alkylating", "antimetabolite", "antineoplastic",
    "aromatase inhibitor", "antiandrogen",
    "checkpoint inhibitor", "programmed death", "pd-1", "pd-l1", "ctla-4",
    "btk inhibitor", "pi3k inhibitor", "mek inhibitor", "braf inhibitor",
    "monoclonal antibody", "immunostimulant", "immunosuppressant",
    "interleukin", "interferon",
    "estrogen receptor", "androgen receptor", "somatostatin",
    "radioactive therapeutic", "photodynamic",
    "egfr", "vegfr", "her2",
]


def is_oncology_pharm_class(epc_list: list[str]) -> bool:
    """通过 EPC 分类判断是否为抗肿瘤相关"""
    for epc in epc_list:
        t = epc.lower()
        if any(p in t for p in ONCOLOGY_EPC_PATTERNS):
            return True
    return False


def is_candidate_oncology_drug(brand: str, generic: str, epc_list: list[str]) -> bool:
    """综合判断是否为候选抗肿瘤药"""
    if is_oncology_pharm_class(epc_list):
        return True
    name = f"{brand} {generic}".lower()
    oncology_hints = [
        "ib", "mab", "nib", "ciclib", "parib",  # common oncology suffixes
        "抗癌", "肿瘤", "癌",                    # Chinese markers
    ]
    # Check common oncology suffix patterns
    for hint in oncology_hints:
        if hint in name:
            return True
    return False


async def _rate_limited_get(
    client: httpx.AsyncClient,
    url: str,
    params: dict | None = None,
) -> dict | None:
    """带限速的 GET 请求"""
    global _last_request_time
    for attempt in range(MAX_RETRIES):
        # 基于时间戳的限速，无需跨事件循环锁
        async with _rate_lock:
            now = time.monotonic()
            since_last = now - _last_request_time
            if since_last < RATE_LIMIT_INTERVAL:
                await asyncio.sleep(RATE_LIMIT_INTERVAL - since_last)
            _last_request_time = time.monotonic()

        try:
            resp = await client.get(url, params=params, timeout=60.0)
            if resp.status_code == 404:
                return None
            if resp.status_code == 429:
                wait = 10 * (attempt + 1)
                logger.warning("FDA API rate limited, waiting %ds", wait)
                await asyncio.sleep(wait)
                continue
            resp.raise_for_status()
            return resp.json()
        except httpx.TimeoutException:
            if attempt < MAX_RETRIES - 1:
                await asyncio.sleep(3 * (attempt + 1))
                continue
            logger.error("FDA API timeout after %d retries: %s", MAX_RETRIES, url)
            return None
        except httpx.HTTPStatusError as e:
            body = e.response.text[:500] if e.response else "N/A"
            logger.error(
                "FDA API HTTP error %s: %s | params=%s | body=%s",
                e.response.status_code, url, params, body,
            )
            return None
    return None


# 抗肿瘤药搜索查询 — 用 pharmacology class + oncology 关键词覆盖绝大多数
# 注意：FDA search endpoint 不区分大小写，+AND+ 表示 AND，空格表示 OR
ONCOLOGY_DRUG_QUERIES = [
    # 按 EPC 分类搜索（高精度）
    "openfda.pharm_class_epc:Antineoplastic",
    "openfda.pharm_class_epc:\"Kinase Inhibitor\"",
    "openfda.pharm_class_epc:\"Monoclonal Antibody\"",
    "openfda.pharm_class_epc:\"CDK Inhibitor\"",
    "openfda.pharm_class_epc:\"PARP Inhibitor\"",
    "openfda.pharm_class_epc:\"HDAC Inhibitor\"",
    "openfda.pharm_class_epc:\"Proteasome Inhibitor\"",
    "openfda.pharm_class_epc:\"mTOR Inhibitor\"",
    "openfda.pharm_class_epc:\"Checkpoint Inhibitor\"",
    "openfda.pharm_class_epc:\"Programmed Death\"",
    "openfda.pharm_class_epc:\"CTLA-4\"",
    "openfda.pharm_class_epc:\"BTK Inhibitor\"",
    "openfda.pharm_class_epc:\"VEGF\"",
    "openfda.pharm_class_epc:\"EGFR\"",
    "openfda.pharm_class_epc:\"HER2\"",
    "openfda.pharm_class_epc:\"ALK\"",
    "openfda.pharm_class_epc:\"BRAF\"",
    "openfda.pharm_class_epc:\"MEK Inhibitor\"",
    "openfda.pharm_class_epc:\"FLT3\"",
    "openfda.pharm_class_epc:\"IDH\"",
    # 补充：按品牌名常见肿瘤药后缀
    "openfda.brand_name:*mib AND submissions.submission_type:ORIG",
    "openfda.brand_name:*nib AND submissions.submission_type:ORIG",
    "openfda.brand_name:*parib AND submissions.submission_type:ORIG",
    "openfda.brand_name:*ciclib AND submissions.submission_type:ORIG",
]


async def search_oncology_drugs(
    client: httpx.AsyncClient,
) -> list[dict]:
    """搜索 FDA 数据库中的抗肿瘤药审批记录

    策略：用多个 oncology EPC 分类查询 + 名称后缀查询，去重后返回完整记录。
    search endpoint 支持 skip/limit 分页。
    """
    seen_app_numbers: set[str] = set()
    all_results: list[dict] = []

    for query in ONCOLOGY_DRUG_QUERIES:
        skip = 0
        page_size = 100
        total = 0

        while True:
            data = await _rate_limited_get(
                client,
                f"{FDA_API_BASE}/drug/drugsfda.json",
                {"search": query, "skip": skip, "limit": page_size},
            )
            if not data or not data.get("results"):
                break

            if total == 0:
                total = data.get("meta", {}).get("results", {}).get("total", 0)
                if total == 0:
                    break
                logger.info("Query %r: %d total results", query[:50], total)

            batch = data["results"]
            for r in batch:
                app_num = r.get("application_number", "")
                if app_num and app_num not in seen_app_numbers:
                    # 只保留 NDA/BLA（跳过 ANDA）
                    if app_num.startswith(("NDA", "BLA")):
                        seen_app_numbers.add(app_num)
                        all_results.append(r)

            if len(batch) < page_size:
                break
            skip += page_size

        logger.info("  query done, %d unique NDA/BLA so far", len(seen_app_numbers))

    logger.info(
        "Found %d unique oncology NDA/BLA records", len(all_results),
    )
    return all_results


async def search_recent_nda_approvals(
    client: httpx.AsyncClient,
    since_date: date,
    skip: int = 0,
    limit: int = 100,
) -> dict | None:
    """查询指定日期后的 NDA/BLA ORIG 提交"""
    date_str = since_date.isoformat().replace("-", "")
    # 注意：httpx 会编码 [ 为 %5B，破坏 FDA API 的 range 语法。
    # 因此直接构建带已编码 search 的 URL。
    search = (
        f"(submissions.submission_type:ORIG)"
        f"+AND+(submissions.submission_status_date:%5B{date_str}+TO+99999999%5D)"
    )
    url = f"{FDA_API_BASE}/drug/drugsfda.json?search={search}&skip={skip}&limit={limit}"
    return await _rate_limited_get(client, url)


def extract_or_orig_submission(submissions: list[dict]) -> dict | None:
    """提取 ORIG（原始申请）提交记录"""
    for s in submissions:
        if s.get("submission_type") == "ORIG":
            return s
    return None


def extract_efficacy_supplements(submissions: list[dict]) -> list[dict]:
    """提取 EFFICACY（新适应症）补充提交"""
    return [
        s for s in submissions
        if s.get("submission_type") == "SUPPL"
        and s.get("submission_class_code") == "EFFICACY"
        and s.get("submission_status") == "AP"
    ]


def parse_fda_date(date_str: str | None) -> date | None:
    """解析 FDA 日期格式 YYYYMMDD → date"""
    if not date_str or len(date_str) != 8:
        return None
    try:
        return date(
            int(date_str[:4]), int(date_str[4:6]), int(date_str[6:8]),
        )
    except (ValueError, IndexError):
        return None
