"""全局文献库模型：global_literature, global_tags, global_literature_tags, global_journals"""

import uuid
from datetime import date, datetime

from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Index, Integer, String, Text, Uuid, UniqueConstraint, func
from sqlalchemy.dialects.postgresql import JSONB, TSVECTOR
from sqlalchemy.orm import Mapped, mapped_column

from app.db import Base, new_uuid


class GlobalLiterature(Base):
    __tablename__ = "global_literature"

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=new_uuid)
    pmid: Mapped[int] = mapped_column(Integer, unique=True, nullable=False)
    title: Mapped[str] = mapped_column(Text, nullable=False)
    abstract: Mapped[str | None] = mapped_column(Text)
    authors: Mapped[dict] = mapped_column(JSONB, default=list)
    doi: Mapped[str | None] = mapped_column(String(500))
    journal: Mapped[str | None] = mapped_column(String(500))
    journal_issn: Mapped[str | None] = mapped_column(String(20))
    journal_iso: Mapped[str | None] = mapped_column(String(100))            # NLM ISO 缩写 (e.g. "N Engl J Med")
    volume: Mapped[str | None] = mapped_column(String(50))
    issue: Mapped[str | None] = mapped_column(String(50))
    pages: Mapped[str | None] = mapped_column(String(100))
    pub_date: Mapped[date | None] = mapped_column(Date)                 # 纸质出版日期（期刊卷期日期，纯电子刊则为电子日期）【PubMed: PubDate】
    print_date: Mapped[date | None] = mapped_column(Date)               # 纸质出版日期（仅纸质版见刊日期）【PubMed: PPDAT】
    pub_year: Mapped[int | None] = mapped_column(Integer)
    pub_types: Mapped[dict] = mapped_column(JSONB, default=list)
    mesh_headings: Mapped[dict] = mapped_column(JSONB, default=list)
    language: Mapped[str] = mapped_column(String(10), default="en")
    raw_xml_hash: Mapped[str | None] = mapped_column(String(64))

    # ═══ 新增字段 ═══
    pmc_id: Mapped[str | None] = mapped_column(String(20))           # PMC1234567
    is_oa: Mapped[bool] = mapped_column(default=False)               # 开放存取
    keywords: Mapped[dict] = mapped_column(JSONB, default=list)       # 作者关键词
    pubmed_revised: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))  # PubMed 修订日期
    grants: Mapped[dict] = mapped_column(JSONB, default=list)         # 基金资助
    cited_by_count: Mapped[int] = mapped_column(Integer, default=0)   # 引用次数
    full_text_sections: Mapped[dict | None] = mapped_column(JSONB)     # PMC OA 全文结构化段落（旧，不断新增存 COS）
    full_text_path: Mapped[str | None] = mapped_column(String(255))   # COS 对象键，全文 JSON 新数据存这里
    trial_reg: Mapped[dict | None] = mapped_column(JSONB)               # 临床试验注册号 {nct, eudract, chictr}
    study_design: Mapped[dict | None] = mapped_column(JSONB)            # 研究设计分类 {primary, sub, design}
    pico: Mapped[dict | None] = mapped_column(JSONB)                    # PICO {population, intervention, comparator, outcome, sample_size, effect_size}
    citation_status: Mapped[str | None] = mapped_column(String(20))    # MedlineCitation Status: publisher/in-process/medline/...
    date_completed: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))  # MeSH 标引完成日期
    retracted: Mapped[bool] = mapped_column(Boolean, default=False)    # 是否被撤稿
    retraction_details: Mapped[dict | None] = mapped_column(JSONB)       # 撤稿详情 {ref_type, ref_pmid, ref_source}
    license_info: Mapped[dict | None] = mapped_column(JSONB)              # 许可信息 {href, type, text}
    pre_extracted_data: Mapped[dict | None] = mapped_column(JSONB)        # Table 1 预提取基线数据
    is_negative_result: Mapped[bool] = mapped_column(Boolean, default=False)  # 阴性结果
    rct_detection: Mapped[dict | None] = mapped_column(JSONB)              # RCT 检测结果 {is_rct, confidence, source, evidence}
    search_tsv: Mapped[str | None] = mapped_column(TSVECTOR)               # 全文检索向量（PG tsvector）
    negative_result_details: Mapped[dict | None] = mapped_column(JSONB)   # 阴性结果详情
    ai_summary: Mapped[dict | None] = mapped_column(JSONB)                  # AI 摘要 {one_liner, structured, implication}

    # ═══ PubMed 补充解析字段 ═══
    chemical_list: Mapped[dict] = mapped_column(JSONB, default=list)        # ChemicalList [{name, registry_number, mesh_ui}]
    gene_symbols: Mapped[dict] = mapped_column(JSONB, default=list)          # GeneSymbolList
    num_refs: Mapped[int | None] = mapped_column(Integer)                   # NumberOfReferences
    publication_status: Mapped[str | None] = mapped_column(String(30))      # PublicationStatus (epublish/ppublish/aheadofprint)
    article_date: Mapped[date | None] = mapped_column(Date)                 # 电子出版日期（在线先发，早于纸质版）【PubMed: ArticleDate】
    create_date: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))  # PubMed 记录创建日期【PubMed: CRDT】
    entrez_date: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))  # PubMed 收录日期【PubMed: EDAT】
    databank_list: Mapped[dict] = mapped_column(JSONB, default=list)         # DataBankList [{databank_name, accession_numbers}]
    suppl_mesh_list: Mapped[dict] = mapped_column(JSONB, default=list)       # SupplMeshList [{descriptor, ui, type}]

    source: Mapped[str] = mapped_column(String(30), default="pubmed_ftp")
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

    __table_args__ = (
        Index("ix_gl_pub_date", "pub_date"),
        Index("ix_gl_pub_year", "pub_year"),
        Index("ix_gl_journal_issn", "journal_issn"),
        Index("ix_gl_created", "created_at"),
        Index("ix_gl_pmc_id", "pmc_id"),
        Index("ix_gl_search_tsv", "search_tsv", postgresql_using="gin"),
    )


class GlobalTag(Base):
    __tablename__ = "global_tags"

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=new_uuid)
    mesh_ui: Mapped[str | None] = mapped_column(String(20))
    name_zh: Mapped[str | None] = mapped_column(String(200))
    name_en: Mapped[str | None] = mapped_column(String(200))
    path: Mapped[str] = mapped_column(String(500), nullable=False)
    level: Mapped[int] = mapped_column(Integer, default=1)
    parent_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, ForeignKey("global_tags.id", ondelete="CASCADE"))
    tree_number: Mapped[str | None] = mapped_column(String(100))
    entry_terms: Mapped[dict | None] = mapped_column(JSONB)  # MeSH 入口词列表（~200k，从 NLM desc2025.asc 导入）
    tag_category: Mapped[str] = mapped_column(String(30), default="cancer")
    sort_order: Mapped[int] = mapped_column(Integer, default=0)
    icon: Mapped[str | None] = mapped_column(String(50))
    is_selectable: Mapped[bool] = mapped_column(Boolean, default=True)
    article_count: Mapped[int] = mapped_column(Integer, default=0)
    source: Mapped[str] = mapped_column(String(20), default="auto")
    # "manual" = 手工维护（种子数据 / 后台新增）
    # "mesh"   = C04 MeSH 批量导入（NLM 权威来源）
    # "auto"   = 管道懒创建（待审核）
    is_active: Mapped[bool] = mapped_column(Boolean, default=False)
    # True  = 已确认，对用户可见
    # False = 未确认，仅后台可见
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

    __table_args__ = (
        Index("ix_gt_category", "tag_category"),
        Index("ix_gt_mesh_ui", "mesh_ui"),
        Index("ix_gt_path", "path"),
    )


class GlobalLiteratureTag(Base):
    __tablename__ = "global_literature_tags"

    literature_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("global_literature.id", ondelete="CASCADE"), primary_key=True)
    tag_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("global_tags.id", ondelete="CASCADE"), primary_key=True)
    is_major: Mapped[bool] = mapped_column(Boolean, default=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    __table_args__ = (
        Index("ix_glt_tag", "tag_id"),
        Index("ix_glt_literature", "literature_id"),
    )


class GlobalTagTreeNumber(Base):
    """MeSH Descriptor → TreeNumber 多对多映射

    一个 MeSH Descriptor（global_tags）可以有多个树号（如 C04.588.894.797.520）。
    用于 [MH] 搜索展开（tree_number LIKE 前缀匹配）和 C04 肿瘤分类判定。
    """
    __tablename__ = "global_tag_tree_numbers"

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=new_uuid)
    tag_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("global_tags.id", ondelete="CASCADE"), nullable=False)
    tree_number: Mapped[str] = mapped_column(String(100), nullable=False)
    tree_category: Mapped[str] = mapped_column(String(10), nullable=False)  # C04, C08, C15, ...
    is_primary: Mapped[bool] = mapped_column(Boolean, default=False)

    __table_args__ = (
        Index("ix_gttn_tag_id", "tag_id"),
        Index("ix_gttn_tree_category", "tree_category"),
        Index("ix_gttn_tree_number", "tree_number"),
        UniqueConstraint("tag_id", "tree_number", name="uq_gttn_tag_tree"),
    )


class GlobalJournal(Base):
    __tablename__ = "global_journals"

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=new_uuid)
    name: Mapped[str] = mapped_column(String(500), nullable=False)
    issn: Mapped[str | None] = mapped_column(String(20))
    eissn: Mapped[str | None] = mapped_column(String(20))
    tier: Mapped[str] = mapped_column(String(5), default="4")
    impact_factor: Mapped[float | None] = mapped_column()
    publisher: Mapped[str | None] = mapped_column(String(300))
    article_count: Mapped[int] = mapped_column(Integer, default=0)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    priority_score: Mapped[float | None] = mapped_column()       # 综合优先级评分 0-100
    priority_source: Mapped[str | None] = mapped_column(String(20), default="auto")  # 'auto' | 'manual'
    specialty: Mapped[str | None] = mapped_column(String(50))    # 'oncology' | NULL
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

    __table_args__ = (
        Index("ix_gj_tier", "tier"),
        Index("ix_gj_priority", "priority_score", postgresql_using="btree"),
        UniqueConstraint("issn", name="uq_gj_issn"),
        UniqueConstraint("name", name="uq_gj_name"),
    )
