"""AI 摘要引擎：DeepSeek 批量生成中文摘要 + 翻译"""

import asyncio
import hashlib
import logging
import time

import httpx

from app.config import settings

logger = logging.getLogger(__name__)

AI_API_KEY = settings.AI_API_KEY or None
AI_BASE_URL = settings.AI_BASE_URL
AI_MODEL = settings.AI_MODEL
MAX_CONCURRENT = 3  # 并发数
CACHE: dict[str, str] = {}  # 简单内存缓存（生产换 Redis）

_AI_CONFIG_CACHE: dict[str, str] = {}
_AI_CONFIG_CACHE_TIME: float = 0


async def _load_ai_config(db) -> dict | None:
    """从 DB 加载 AI 配置（60 秒缓存）"""
    global _AI_CONFIG_CACHE, _AI_CONFIG_CACHE_TIME
    if db is None:
        return None
    now = time.time()
    if _AI_CONFIG_CACHE and now - _AI_CONFIG_CACHE_TIME < 60:
        return _AI_CONFIG_CACHE
    from sqlalchemy import select
    from app.models.business import AiProviderConfig
    cfg = (await db.execute(
        select(AiProviderConfig).where(AiProviderConfig.is_active == True)
        .order_by(AiProviderConfig.created_at.desc()).limit(1)
    )).scalar()
    if cfg:
        _AI_CONFIG_CACHE = {
            "api_key": cfg.api_key,
            "base_url": cfg.base_url,
            "model": cfg.model_name,
            "temperature": cfg.temperature / 10.0,
        }
    else:
        _AI_CONFIG_CACHE = {}
    _AI_CONFIG_CACHE_TIME = now
    return _AI_CONFIG_CACHE


async def _call_ai(prompt: str, max_tokens: int = 300, db=None) -> str | None:
    """调用 AI API（DeepSeek / OpenAI 兼容），有 db 时优先使用 DB 配置"""
    api_key = AI_API_KEY
    base_url = AI_BASE_URL
    model = AI_MODEL
    temperature = 0.3

    if db is not None:
        cfg = await _load_ai_config(db)
        if cfg and cfg.get("api_key"):
            api_key = cfg["api_key"]
            base_url = cfg.get("base_url") or base_url
            model = cfg.get("model") or model
            temperature = cfg.get("temperature", 0.3)

    if not api_key:
        return None
    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
    payload = {
        "model": model,
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": max_tokens,
        "temperature": temperature,
    }
    try:
        async with httpx.AsyncClient(timeout=30) as client:
            r = await client.post(f"{base_url}/chat/completions", json=payload, headers=headers)
            r.raise_for_status()
            data = r.json()
            return data["choices"][0]["message"]["content"].strip()
    except Exception:
        logger.exception("AI call failed")
        return None


async def generate_one_liner(title: str, abstract: str | None, db=None) -> str | None:
    """生成一句话中文总结（≤50字）"""
    if not abstract:
        return None

    cache_key = hashlib.md5(abstract.encode("utf-8")[:500]).hexdigest()
    if cache_key in CACHE:
        return CACHE[cache_key]

    prompt = f"""你是肿瘤科文献审稿人。请用中文一句话总结以下论文（≤50字）。
如果涉及药物或治疗方案，请标注通用名；如果是临床试验，请标注是否为3期。

标题: {title[:200]}
摘要: {abstract[:800]}

中文一句话总结:"""

    result = await _call_ai(prompt, max_tokens=80, db=db)
    if result:
        CACHE[cache_key] = result
    return result


async def generate_structured_summary(title: str, abstract: str | None, db=None) -> str | None:
    """生成结构化中文摘要：背景-方法-结果-结论"""
    if not abstract:
        return None

    cache_key = hashlib.md5(("struct:" + abstract).encode("utf-8")[:500]).hexdigest()
    if cache_key in CACHE:
        return CACHE[cache_key]

    prompt = f"""你是肿瘤科文献审稿人。请将以下英文摘要用中文重新组织为四段式结构化摘要：

1. 背景：1句话
2. 方法：1-2句话
3. 结果：2-3句话（标注关键数据和统计学意义）
4. 结论：1句话（说明临床意义）

标题: {title[:300]}
摘要: {abstract[:1500]}

结构化中文摘要:"""

    result = await _call_ai(prompt, max_tokens=400, db=db)
    if result:
        CACHE[cache_key] = result
    return result


async def translate_text(text: str, direction: str = "en2zh", db=None) -> str | None:
    """翻译标题/摘要"""
    cache_key = hashlib.md5((direction + text).encode("utf-8")[:500]).hexdigest()
    if cache_key in CACHE:
        return CACHE[cache_key]

    if direction == "en2zh":
        prompt = f"请将以下英文翻译为专业中文医学术语:\n\n{text[:1000]}\n\n中文翻译:"
    else:
        prompt = f"Translate the following Chinese to professional English medical terminology:\n\n{text[:1000]}\n\nEnglish:"

    result = await _call_ai(prompt, max_tokens=300, db=db)
    if result:
        CACHE[cache_key] = result
    return result


async def generate_clinical_implication(title: str, abstract: str | None, db=None) -> str | None:
    """生成"对我的临床实践意味着什么"的解读"""
    if not abstract:
        return None

    cache_key = hashlib.md5(("implication:" + abstract).encode("utf-8")[:500]).hexdigest()
    if cache_key in CACHE:
        return CACHE[cache_key]

    prompt = f"""你是一位肿瘤科主治医师。请用中文简要回答（≤80字）：
"这项研究对我的临床实践意味着什么？我应该改变目前的治疗方案吗？"

标题: {title[:200]}
摘要: {abstract[:800]}

回答:"""

    result = await _call_ai(prompt, max_tokens=120, db=db)
    if result:
        CACHE[cache_key] = result
    return result


async def extract_pico(title: str, abstract: str | None, full_text_sections: dict | None = None, db=None) -> dict | None:
    """从摘要 + 全文提取 PICO（Population, Intervention, Comparator, Outcome）+ 样本量 + 效应量"""
    if not abstract and not full_text_sections:
        return None

    context = f"标题: {title[:300]}\n"
    if abstract:
        context += f"摘要: {abstract[:2000]}\n"
    if full_text_sections:
        sections = full_text_sections.get("sections", [])
        fts = "\n".join(
            f"{s.get('heading', '')}: {s.get('text', '')[:500]}"
            for s in sections[:5]
        )
        context += f"全文段落:\n{fts[:3000]}\n"

    prompt = f"""你是肿瘤科 Meta 分析研究员。请从以下文献中提取 PICO 要素。

要求：
1. population: 研究对象（人群/癌种/分期/样本量）
2. intervention: 干预措施（药名+剂量+方案）
3. comparator: 对照措施（对照组治疗）
4. outcome: 主要终点和关键次要终点
5. sample_size: 总样本量（整数，无则写 null）
6. effect_size: 主要效应量（HR / OR / RR / 95% CI / p 值，无则写 null）

请以 JSON 格式输出，不要加额外文字：
{{
  "population": "...",
  "intervention": "...",
  "comparator": "...",
  "outcome": "...",
  "sample_size": null,
  "effect_size": null
}}

{context}"""

    result = await _call_ai(prompt, max_tokens=600, db=db)
    if not result:
        return None

    import json
    try:
        # 尝试提取 JSON block
        start = result.find("{")
        end = result.rfind("}")
        if start >= 0 and end > start:
            parsed = json.loads(result[start:end+1])
            # 验证并清理
            for key in ("population", "intervention", "comparator", "outcome", "sample_size", "effect_size"):
                if key not in parsed:
                    parsed[key] = None
            return parsed
    except (json.JSONDecodeError, Exception):
        logger.warning("Failed to parse PICO JSON from AI response")
        return None


async def batch_summarize(articles: list[dict], mode: str = "one_liner", db=None) -> list[dict]:
    """批量生成摘要（3并发）"""
    sem = asyncio.Semaphore(MAX_CONCURRENT)

    async def process(a: dict) -> dict:
        async with sem:
            if mode == "one_liner":
                a["ai_summary"] = await generate_one_liner(a.get("title", ""), a.get("abstract"), db=db)
            elif mode == "structured":
                a["ai_summary"] = await generate_structured_summary(a.get("title", ""), a.get("abstract"), db=db)
            elif mode == "translate":
                a["title_zh"] = await translate_text(a.get("title", ""), db=db)
                a["abstract_zh"] = await translate_text(a.get("abstract", ""), "en2zh", db=db) if a.get("abstract") else None
            elif mode == "implication":
                a["clinical_implication"] = await generate_clinical_implication(a.get("title", ""), a.get("abstract"), db=db)
        return a

    tasks = [process(a) for a in articles]
    return await asyncio.gather(*tasks)
