"""适应症文本结构化解析 — 从 FDA Label 中提取癌种、线数、联合方案

用法：
  from app.services.indication_parser import parse_all_labels
  await parse_all_labels()
"""

import logging
import re

logger = logging.getLogger(__name__)

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

from app.db import async_session
from app.models.business import DrugLabel
from app.models.literature import GlobalTag

# ── 治疗线数关键词匹配 ──
LINE_PATTERNS = [
    (r"(?i)\bfirst[- ]line\b|\b1st[- ]line\b|\b1l\b", "1L"),
    (r"(?i)\bsecond[- ]line\b|\b2nd[- ]line\b|\b2l\b", "2L"),
    (r"(?i)\bthird[- ]line\b|\b3rd[- ]line\b|\b3l\b", "3L"),
    (r"(?i)\bfourth[- ]line\b|\b4th[- ]line\b|\b4l\b", "4L"),
    (r"(?i)\badjuvant\b", "adjuvant"),
    (r"(?i)\bneoadjuvant\b", "neoadjuvant"),
    (r"(?i)\bmaintenance\b", "maintenance"),
    (r"(?i)\bfront[- ]line\b", "1L"),
]

# ── 联合方案关键词 ──
COMBINATION_KEYWORDS = [
    r"(?i)in combination with\s+([\w\s\-]+?)(?:[,;.]|and|or|$)",
    r"(?i)combined with\s+([\w\s\-]+?)(?:[,;.]|and|or|$)",
    r"(?i)plus\s+([\w\s\-]+?)(?:[,;.]|and|or|$)",
    r"(?i)as a component of\s+([\w\s\-]+?)(?:[,;.]|and|or|$)",
]


def _extract_line(text: str) -> list[str]:
    """提取治疗线数"""
    lines = set()
    for pattern, label in LINE_PATTERNS:
        if re.search(pattern, text):
            lines.add(label)
    return sorted(lines)


def _extract_combinations(text: str) -> list[str]:
    """提取联合用药"""
    combos = set()
    for pattern in COMBINATION_KEYWORDS:
        for m in re.finditer(pattern, text):
            drug = m.group(1).strip().rstrip(".,; ")
            if drug and len(drug) < 100:
                combos.add(drug)
    return sorted(combos)


def _match_cancer_types(text: str, cancer_tags: list[dict]) -> list[dict]:
    """从文本中匹配癌种标签

    对每个 cancer tag 的 name_en 和 name_zh 做不区分大小写的子串匹配。
    优先匹配 name_en（FDA label 都是英文），按子串长度降序避免短名误匹配。
    """
    if not text:
        return []

    lower_text = text.lower()
    matched = []

    for tag in cancer_tags:
        # 排除顶层"癌种"和太短的通用名
        names = []
        if tag["name_en"] and len(tag["name_en"]) > 2:
            names.append(tag["name_en"])
        if tag["name_zh"] and len(tag["name_zh"]) > 1:
            names.append(tag["name_zh"])

        for name in names:
            if name.lower() in lower_text:
                matched.append({
                    "cancer_type": tag["name_en"] or name,
                    "name_zh": tag["name_zh"],
                    "tag_id": tag["id"],
                    "path": tag["path"],
                    "matched_term": name,
                })
                break  # 每个标签只匹配一次

    # 按匹配名称长度降序排序（长匹配更精确）
    matched.sort(key=lambda x: -len(x["matched_term"]))
    return matched


async def parse_all_labels() -> dict:
    """遍历所有 DrugLabel，解析适应症文本"""
    stats = {"parsed": 0, "skipped": 0, "errors": 0}

    async with async_session() as db:
        # 1. 加载所有 cancer 类全局标签
        result = await db.execute(
            select(GlobalTag)
            .where(GlobalTag.tag_category == "cancer", GlobalTag.is_active == True)
            .order_by(GlobalTag.path)
        )
        cancer_tags = [
            {
                "id": str(t.id),
                "name_zh": t.name_zh,
                "name_en": t.name_en,
                "path": t.path,
            }
            for t in result.scalars().all()
        ]
        logger.info("Loaded %d cancer tags", len(cancer_tags))

        # 2. 遍历有 indications_and_usage 的 label
        labels = await db.execute(
            select(DrugLabel)
            .where(DrugLabel.indications_and_usage.isnot(None))
            .order_by(DrugLabel.drug_name)
        )
        labels = labels.scalars().all()

        for label in labels:
            try:
                text = label.indications_and_usage or ""
                if not text.strip():
                    stats["skipped"] += 1
                    continue

                matched_cancers = _match_cancer_types(text, cancer_tags)
                lines = _extract_line(text)
                combinations = _extract_combinations(text)

                parsed = {
                    "cancers": matched_cancers,
                    "lines": lines,
                    "combinations": combinations,
                }
                label.parsed_indications = parsed
                stats["parsed"] += 1
            except Exception as e:
                logger.error("Error parsing label %s: %s", label.set_id, e)
                stats["errors"] += 1

        await db.commit()

    # 解析完成后自动回填 DrugApproval.cancer_type_tag_id
    if stats["parsed"] > 0:
        async with async_session() as _bf_db:
            from app.services.drug_cancer_mapper import backfill_from_labels
            fixed = await backfill_from_labels(_bf_db)
            if fixed:
                logger.info("Backfilled %d DrugApproval cancer_type_tag_ids from parsed labels", fixed)

    logger.info(
        "Parsed %d labels (%d skipped, %d errors)",
        stats["parsed"], stats["skipped"], stats["errors"],
    )
    return stats
