#!/usr/bin/env python3
"""从已有文献库中提取真实指南数据，填充 guideline_versions + guideline_evidence 表。

策略：查询 global_literature 中 pub_types 含 Guideline/Practice Guideline 的文献，
解析标题提取来源（NCCN/CSCO/ESMO/ASCO/ASTRO）、癌种、版本号，创建指南版本记录，
并将文献本身作为证据关联。

运行：
  cd backend && python scripts/seed_guidelines.py
"""

import asyncio
import re
import uuid
from collections import defaultdict

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

from app.db import async_session
from app.models.business import GuidelineEvidence, GuidelineVersion
from app.models.literature import GlobalLiterature, GlobalTag

# 构建去除特殊商标字符的标题版本
def clean_title(title: str) -> str:
    """去除 ® © ™ 等商标字符，统一空格"""
    t = title.encode('utf-8', errors='ignore').decode('utf-8', errors='ignore')
    for ch in ['®', '©', '™', '°']:  # ® © ™ °
        t = t.replace(ch, '')
    return re.sub(r'\s+', ' ', t).strip()

# 来源检测（ASCO 放前面，避免 "CSCO and ASCO" 先命中 CSCO）
SOURCES = ('NCCN', 'ESMO', 'ASCO', 'ASTRO', 'CSCO')

# NCCN 标题模式集：使用贪心匹配避免 [^,]+? 在可选组前只匹配到 1 字符
NCCN_PATTERNS = [
    re.compile(r'NCCN\s+Guidelines?\s+Insights?:\s+(?P<cancer>[^,]+)(?:,\s+Version\s+(?P<ver>[\d.v]+))?(?:\s|\.|$)', re.I),
    re.compile(r'NCCN\s+Clinical\s+Practice\s+Guidelines?\s+in\s+Oncology:\s+(?P<cancer>[^,]+)(?:,\s+Version\s+(?P<ver>[\d.v]+))?(?:\s|\.|$)', re.I),
    re.compile(r'(?P<cancer>[^,]+),\s+Version\s+(?P<ver>[\d.v]+),\s+NCCN\s+Clinical\s+Practice\s+Guidelines?\s+in\s+Oncology', re.I),
    re.compile(r'NCCN\s+Guidelines?\s+(?:for\s+)?(?:the\s+)?(?:management\s+of\s+)?(?P<cancer>[^,\.]+)', re.I),
    re.compile(r'NCCN\s+Guidelines?\s+Update\s+for\s+(?P<cancer>[^,\.]+)', re.I),
    re.compile(r'NCCN\s+Guidelines?\s+for\s+(?P<cancer>[^,\.]+)', re.I),
]

ESMO_PATTERNS = [
    re.compile(r'ESMO\s+Clinical\s+Practice\s+Guidelines?\s+for\s+(?:the\s+)?(?:management\s+of\s+)?(?:patients\s+with\s+)?(?P<cancer>[^\.]+)', re.I),
    re.compile(r'ESMO\s+Guidelines?:\s+(?P<cancer>[^\.]+)', re.I),
    re.compile(r'(?:Pan-Asian\s+)?adapted\s+ESMO\s+Clinical\s+Practice\s+Guidelines?\s+for\s+(?:the\s+)?(?:management\s+of\s+)?(?:patients\s+with\s+)?(?P<cancer>[^\.:]+)', re.I),
]

ASCO_PATTERNS = [
    re.compile(r'ASCO\s+(?:Clinical\s+Practice\s+)?Guidelines?\s+(?:for\s+)?(?:the\s+)?(?:management\s+of\s+)?(?:patients\s+with\s+)?(?P<cancer>[^\.]+)', re.I),
    re.compile(r'ASCO\s+endorsement\s+of\s+(?P<cancer>[^\.]+)', re.I),
    re.compile(r'ASCO\s+.*?Guidelines?\s+for\s+(?P<cancer>[^\.]+)', re.I),
]

ASTRO_PATTERNS = [
    re.compile(r'(?:Executive\s+Summary\s+of\s+)?[Aa]n?\s+ASTRO\s+Clinical\s+Practice\s+Guidelines?'),
    re.compile(r'ASTRO\s+(?:Radiation\s+Therapy\s+)?Summary\s+of\s+the\s+'),
    re.compile(r'ASTRO\s+.*?Guidelines?\s+for\s+(?P<cancer>[^\.]+)', re.I),
]

# ASTRO 癌种提取（从标题前缀 "Radiation Therapy for XXX:"）
ASTRO_CANCER_RE = re.compile(r'(?:for|of)\s+(?:the\s+)?(?:treatment\s+of\s+)?(?:primary\s+)?(?P<cancer>[^:]+?)(?::|$)', re.I)
# NCCN 癌种回退（标题开头）
FALLBACK_NCCN_RE = re.compile(r'^(?P<cancer>[^,]+?)(?:,\s+Version|\s*\||$)', re.I)


def extract_meta(title: str) -> dict | None:
    c = clean_title(title)
    upper = c.upper()

    # 确定来源（优先匹配实际撰写指南的学会，而非共同背书）
    source = None
    for s in SOURCES:
        if s in upper:
            source = s
            break
    if not source:
        return None

    patterns = {
        'NCCN': NCCN_PATTERNS,
        'ESMO': ESMO_PATTERNS,
        'ASCO': ASCO_PATTERNS,
        'ASTRO': ASTRO_PATTERNS,
        'CSCO': [],
    }.get(source, [])

    cancer_raw = None
    version = None

    for pat in patterns:
        m = pat.search(c)
        if m:
            cancer_raw = m.groupdict().get('cancer', '').strip().rstrip('.,; ')
            ver = m.groupdict().get('ver', '')
            if ver:
                version = ver.strip()
            break

    # ASTRO 特殊处理：从标题前缀 "for XXX Cancer:" 提取癌种
    if source == 'ASTRO' and not cancer_raw:
        m2 = ASTRO_CANCER_RE.search(c)
        if m2:
            cancer_raw = m2.group('cancer').strip().rstrip('.,; ')

    # NCCN 回退：从标题开头提取癌种
    if source == 'NCCN' and not cancer_raw:
        m = FALLBACK_NCCN_RE.search(c)
        if m:
            cancer_raw = m.group('cancer').strip()

    if not cancer_raw:
        return None

    # 提取版本
    if not version:
        vm = re.search(r'Version\s+([\d.v]+)', c, re.IGNORECASE)
        if vm:
            version = vm.group(1)

    return {
        'source': source,
        'cancer_raw': cancer_raw,
        'version': version or '',
    }


def extract_version(title: str) -> str:
    c = clean_title(title)
    m = re.search(r'Version\s+([\d.v]+)', c, re.IGNORECASE)
    if m:
        return m.group(1)
    m = re.search(r'(\d{4})\s+Update', c, re.IGNORECASE)
    if m:
        return m.group(1)
    return ''


CANCER_KEYWORD_MAP: dict[str, str] = {
    'non-small cell lung': 'Non-Small Cell Lung Cancer',
    'nsclc': 'Non-Small Cell Lung Cancer',
    'small cell lung': 'Small Cell Lung Cancer',
    'sclc': 'Small Cell Lung Cancer',
    'lung cancer screening': 'Lung Cancer Screening',
    'lung cancer': 'Lung Cancer',
    'mesothelioma': 'Mesothelioma',
    'breast cancer': 'Breast Cancer',
    'breast': 'Breast Cancer',
    'gastric cancer': 'Gastric Cancer',
    'gastric': 'Gastric Cancer',
    'stomach': 'Gastric Cancer',
    'colorectal cancer': 'Colorectal Cancer',
    'colorectal': 'Colorectal Cancer',
    'colon cancer': 'Colorectal Cancer',
    'rectal cancer': 'Rectal Cancer',
    'hepatocellular carcinoma': 'Hepatocellular Carcinoma',
    'hepatocellular': 'Hepatocellular Carcinoma',
    'hcc': 'Hepatocellular Carcinoma',
    'liver cancer': 'Hepatocellular Carcinoma',
    'liver': 'Hepatocellular Carcinoma',
    'pancreatic cancer': 'Pancreatic Cancer',
    'pancreatic': 'Pancreatic Cancer',
    'prostate cancer': 'Prostate Cancer',
    'prostate': 'Prostate Cancer',
    'melanoma': 'Melanoma',
    'ovarian cancer': 'Ovarian Cancer',
    'ovarian': 'Ovarian Cancer',
    'head and neck': 'Head and Neck Cancer',
    'esophageal': 'Esophageal Cancer',
    'esophagus': 'Esophageal Cancer',
    'oesophageal': 'Esophageal Cancer',
    'renal cell carcinoma': 'Renal Cell Carcinoma',
    'renal cell': 'Renal Cell Carcinoma',
    'rcc': 'Renal Cell Carcinoma',
    'kidney cancer': 'Renal Cell Carcinoma',
    'bladder cancer': 'Bladder Cancer',
    'urothelial': 'Bladder Cancer',
    'cervical cancer': 'Cervical Cancer',
    'cervical': 'Cervical Cancer',
    'leukemia': 'Leukemia',
    'lymphoma': 'Lymphoma',
    'hodgkin': 'Hodgkin Lymphoma',
    'multiple myeloma': 'Multiple Myeloma',
    'myeloma': 'Multiple Myeloma',
    'glioma': 'Glioma',
    'glioblastoma': 'Glioblastoma',
    'gbm': 'Glioblastoma',
    'thyroid cancer': 'Thyroid Cancer',
    'endometrial': 'Endometrial Cancer',
    'uterine': 'Endometrial Cancer',
    'soft tissue sarcoma': 'Sarcoma',
    'sarcoma': 'Sarcoma',
    'gist': 'Gastrointestinal Stromal Tumor',
    'neuroendocrine': 'Neuroendocrine Tumor',
    'anal cancer': 'Anal Cancer',
    'nasopharyngeal': 'Nasopharyngeal Carcinoma',
    'brain metastases': 'Brain Metastases',
    'brain tumor': 'Brain Tumor',
    'penile cancer': 'Penile Cancer',
    'vulvar': 'Vulvar Cancer',
    'vaginal': 'Vaginal Cancer',
}


def normalize_cancer_name(raw: str) -> str | None:
    lower = raw.lower().strip()
    for kw, name in sorted(CANCER_KEYWORD_MAP.items(), key=lambda x: -len(x[0])):
        if kw in lower:
            return name
    return None


async def find_cancer_tag(db: AsyncSession, cancer_name: str) -> str | None:
    if not cancer_name:
        return None
    r = await db.execute(
        select(GlobalTag).where(
            GlobalTag.tag_category == 'cancer',
            GlobalTag.is_active == True,
            GlobalTag.name_en == cancer_name,
        )
    )
    tag = r.scalar_one_or_none()
    if tag:
        return str(tag.id)
    r = await db.execute(
        select(GlobalTag).where(
            GlobalTag.tag_category == 'cancer',
            GlobalTag.is_active == True,
            GlobalTag.name_en.ilike(f'%{cancer_name}%'),
        )
    )
    tag = r.scalar_one_or_none()
    if tag:
        return str(tag.id)
    return None


def extract_recommendation(title: str) -> str:
    c = clean_title(title)
    # Strip leading source prefix
    c = re.sub(r'^(?:NCCN|ESMO|ASCO|ASTRO)\s+(?:Guidelines?\s+)?(?:Insights?:\s+)?(?:Update\s+)?(?:for\s+)?', '', c)
    c = re.sub(r',\s+(?:NCCN|ESMO|ASCO|ASTRO)\s+(?:Clinical\s+)?(?:Practice\s+)?(?:Guidelines?\s+)?(?:in\s+Oncology)?(?:\s+©\s*\d{4})?', '', c)
    c = re.sub(r'\s*©\s*\d{4}.*$', '', c)
    c = re.sub(r'(?:Executive\s+Summary\s+of\s+)?an?\s+(?:ASTRO|ASCO)\s+(?:Clinical\s+Practice\s+)?Guidelines?\s*', '', c)
    c = c.strip().rstrip('.')
    if len(c) > 200:
        c = c[:197] + '...'
    return c


async def main():
    print("=== 从文献库导入真实指南数据 ===")

    async with async_session() as db:
        existing = await db.execute(select(func.count(GuidelineVersion.id)))
        existing_count = existing.scalar()
        print(f"现有指南版本: {existing_count}")

        existing_r = await db.execute(
            select(GuidelineVersion.guideline_source, GuidelineVersion.cancer_type_tag_id, GuidelineVersion.version)
        )
        existing_versions = {
            (r.guideline_source, str(r.cancer_type_tag_id) if r.cancer_type_tag_id else '', r.version)
            for r in existing_r
        }

        # 查询所有有 pub_types 的文献
        r = await db.execute(
            select(GlobalLiterature).where(
                GlobalLiterature.pub_types.isnot(None),
                GlobalLiterature.title.isnot(None),
            ).order_by(GlobalLiterature.pub_date.desc().nullslast())
        )
        all_lit = r.scalars().all()
        print(f"总文献数: {len(all_lit)}")

        # 筛选 guideline type
        guideline_lit = []
        for lit in all_lit:
            pts = lit.pub_types or {}
            pt_str = str(pts.values() if isinstance(pts, dict) else pts).lower()
            if 'guideline' in pt_str:
                guideline_lit.append(lit)

        print(f"Guideline 类型文献: {len(guideline_lit)}")

        # 筛选 NCCN/CSCO/ESMO/ASCO/ASTRO
        major_lit = []
        for lit in guideline_lit:
            upper = (lit.title or '').upper()
            if any(s in upper for s in SOURCES):
                major_lit.append(lit)

        print(f"NCCN/CSCO/ESMO/ASCO/ASTRO 文献: {len(major_lit)}")

        created = 0
        skipped = 0
        evidence_count = 0
        by_source: dict[str, list] = defaultdict(list)

        for lit in major_lit:
            title = lit.title or ''
            meta = extract_meta(title)
            if not meta:
                skipped += 1
                continue

            source = meta['source']

            cancer_name = normalize_cancer_name(meta['cancer_raw'])
            if not cancer_name:
                skipped += 1
                continue

            tag_id = await find_cancer_tag(db, cancer_name)
            if not tag_id:
                skipped += 1
                continue

            version = meta['version'] or (str(lit.pub_date.year) if lit.pub_date else '')
            if not version:
                skipped += 1
                continue

            # 去重
            key = (source, tag_id, version)
            if key in existing_versions:
                skipped += 1
                continue

            summary_en = f"Based on: {title[:200]}"
            summary_zh = f"自动生成：基于 {source} {cancer_name} 文献 {title[:80]}"
            gv = GuidelineVersion(
                id=uuid.uuid4(),
                guideline_source=source,
                cancer_type_tag_id=uuid.UUID(tag_id),
                version=version,
                publish_date=lit.pub_date,
                change_summary_en=summary_en,
                change_summary_zh=summary_zh,
            )
            db.add(gv)
            existing_versions.add(key)
            created += 1
            by_source[source].append(cancer_name)

            rec = extract_recommendation(title)
            ev = GuidelineEvidence(
                id=uuid.uuid4(),
                guideline_version_id=gv.id,
                literature_id=lit.id,
                recommendation=rec,
                recommendation_level='',
                treatment_line='',
            )
            db.add(ev)
            evidence_count += 1

        await db.commit()

        print(f"\n=== 导入完成 ===")
        print(f"新建指南版本: {created}")
        print(f"跳过（无法解析/已存在）: {skipped}")
        print(f"新建证据关联: {evidence_count}")
        print(f"\n按来源统计:")
        for src, cancers in sorted(by_source.items()):
            unique = sorted(set(cancers))
            print(f"  {src}: {len(cancers)} 条 — {', '.join(unique)}")
        total = existing_count + created
        print(f"\n总计: {total} 条指南版本")


if __name__ == '__main__':
    asyncio.run(main())
