
"""
LLM 工具模块 — 基于 DeepSeek API 的可复用调用接口

提供不同场景下的 LLM 调用功能：
- analyze: 信息分析
- extract: 信息抽取
- fill: 信息填充
- chat: 通用对话

使用方式：
    from llm.llm_utils import LLM
    llm = LLM()
    result = llm.analyze("分析这段文本...")
"""

from openai import OpenAI
import json
from typing import Optional


class LLM:
    """DeepSeek LLM 调用封装"""

    def __init__(self, model: str = "deepseek-v4-flash"):
        self.client = OpenAI(
            api_key="sk-a329ecd8a64d4db8a8f1c00f89ad169d",
            base_url="https://api.deepseek.com"
        )
        self.model = model

    def _call(
        self,
        system_prompt: str,
        user_prompt: str,
        max_tokens: int = 4096,
        temperature: float = 0.3,
        response_format: Optional[dict] = None,
    ) -> str:
        """
        底层 LLM 调用

        Args:
            system_prompt: 系统提示词
            user_prompt: 用户输入
            max_tokens: 最大输出 token 数
            temperature: 温度参数（分析用低温度，生成用稍高）
            response_format: OpenAI response_format 参数，如 {"type": "json_object"}

        Returns:
            模型输出文本
        """
        kwargs = dict(
            model=self.model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt},
            ],
            max_tokens=max_tokens,
            temperature=temperature,
            stream=False,
        )
        if response_format:
            kwargs["response_format"] = response_format

        response = self.client.chat.completions.create(**kwargs)
        return response.choices[0].message.content

    def analyze(
        self,
        text: str,
        instruction: str = "",
        max_tokens: int = 4096,
    ) -> str:
        """
        信息分析场景 — 对给定文本进行分析、总结、归类

        Args:
            text: 待分析文本
            instruction: 额外的分析指令
            max_tokens: 最大输出长度
        """
        system = (
            "你是一个专业的投标文档分析助手。请基于给定的招标文件和投标文件内容，"
            "进行准确、全面的分析。输出应结构化、条理清晰。使用中文回答。"
        )
        prompt = f"请分析以下内容：\n\n{text}\n\n"
        if instruction:
            prompt += f"额外要求：\n{instruction}"
        return self._call(system, prompt, max_tokens=max_tokens, temperature=0.3)

    def extract(
        self,
        text: str,
        schema_description: str,
        max_tokens: int = 4096,
    ) -> str:
        """
        信息抽取场景 — 从文本中抽取结构化信息

        Args:
            text: 源文本
            schema_description: 需要抽取的信息结构的描述
            max_tokens: 最大输出长度
        """
        system = (
            "你是一个精确的信息抽取助手。从给定的文档内容中提取指定的信息，"
            "输出格式为 JSON。确保数据准确、完整，不要编造不存在的信息。"
            "如果某信息在文内未找到，保留空字符串或 null。"
        )
        prompt = (
            f"请从以下文本中抽取信息，按照要求的格式输出：\n\n"
            f"需要抽取的信息结构：\n{schema_description}\n\n"
            f"源文本：\n{text}\n\n"
            f"请只输出 JSON，不包含其他内容。"
        )
        return self._call(
            system, prompt,
            max_tokens=max_tokens,
            temperature=0.1,
            response_format={"type": "json_object"},
        )

    def fill(
        self,
        template: str,
        context: str,
        max_tokens: int = 4096,
    ) -> str:
        """
        信息填充场景 — 根据上下文填充模板中的占位信息

        Args:
            template: 待填充的模板（含占位符）
            context: 参考上下文（历史投标文件、招标要求等）
            max_tokens: 最大输出长度
        """
        system = (
            "你是一个投标文件撰写助手。根据招标文件要求和历史投标文件参考，"
            "填充模板中的内容。保持专业、准确，符合政府采购投标文件的风格。"
        )
        prompt = (
            f"请根据参考上下文，填充以下模板中的内容：\n\n"
            f"模板：\n{template}\n\n"
            f"参考上下文：\n{context}\n\n"
            f"输出完整的填充结果。"
        )
        return self._call(system, prompt, max_tokens=max_tokens, temperature=0.4)

    def chat(
        self,
        system_prompt: str,
        user_prompt: str,
        max_tokens: int = 4096,
        temperature: float = 0.7,
    ) -> str:
        """通用对话场景 — 自定义 system prompt 和 user prompt"""
        return self._call(system_prompt, user_prompt, max_tokens=max_tokens, temperature=temperature)


def analyze_company_info_needs(tender_text: str, bid_text: str) -> dict:
    """
    基于招标文件和投标文件，分析投标公司需要提供的全部信息，
    返回结构化 JSON。

    Args:
        tender_text: 招标文件全文
        bid_text: 投标文件全文（作为历史参考）

    Returns:
        包含所有需要补充的投标公司信息的 JSON 字典
    """
    llm = LLM()

    system_prompt = (
        "你是一个投标文件智能分析系统。你的任务是基于招标文件（规定格式和要求）"
        "和投标文件（历史示例），分析并列出投标公司需要提供的所有信息和材料。\n\n"
        "请全面分析以下类别，输出为严格的 JSON 格式：\n"
        "1. company_basic: 公司基本信息（名称、地址、联系人等）\n"
        "2. legal_representative: 法定代表人信息（姓名、身份证等）\n"
        "3. qualifications: 资质证书（营业执照、体系认证、专业资质等）\n"
        "4. financial_info: 财务信息（审计报告、纳税证明等）\n"
        "5. social_security: 社保信息（社保缴纳凭证等）\n"
        "6. team_members: 团队人员信息（项目经理、技术负责人等）\n"
        "7. performance: 业绩信息（类似项目合同、业主评价等）\n"
        "8. declarations: 声明函（中小企业声明、无失信声明等）\n"
        "9. pricing: 报价信息（报价明细、成本构成等）\n"
        "10. equipment: 设备资源（拟投入设备、工具等）\n"
        "11. images: 图片材料（资质照片、身份证明扫描件等）\n"
        "12. other: 其他材料\n\n"
        "对于每一项，请包含：\n"
        "- field_name: 字段名（英文驼峰）\n"
        "- label: 中文标签\n"
        "- description: 详细说明（来自招标文件的具体要求）\n"
        "- required: 是否必需（true/false）\n"
        "- source: 来源（'tender_document'=招标文件要求, 'bid_reference'=投标示例中有, 'both'=两者都有）\n"
        "- data_type: 数据类型（text/number/image/file/array）\n"
        "- image_supported: 是否支持图片格式（true/false）\n"
        "- reference_section: 在招标文件中的对应章节\n\n"
        "请确保分析全面、详细，不遗漏任何招标文件中要求的材料。"
    )

    # 构造输入文本（截取关键部分以控制token数量）
    # 招标文件重点：格式要求、评分标准、需求
    # 投标文件重点：实际提供的材料清单
    max_tender_chars = 30000
    max_bid_chars = 30000

    tender_sections = extract_key_sections(tender_text)
    bid_sections = extract_bid_toc(bid_text)

    user_prompt = (
        "请基于下面的招标文件和投标文件内容，分析投标公司需要提供的全部信息和材料。\n\n"
        f"===== 招标文件关键内容（格式要求、评分标准、需求） =====\n"
        f"{tender_sections[:max_tender_chars]}\n\n"
        f"===== 投标文件目录及关键内容（历史参考示例） =====\n"
        f"{bid_sections[:max_bid_chars]}\n\n"
        "请输出严格的 JSON 格式，包含上述所有类别的投标公司信息需求。"
        "重点关注招标文件中明确要求提供的材料，以及投标文件中实际体现的信息类型。"
    )

    result = llm._call(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        max_tokens=8192,
        temperature=0.2,
        response_format={"type": "json_object"},
    )
    return json.loads(result)


def extract_key_sections(tender_text: str) -> str:
    """提取招标文件的关键章节（格式要求、评分标准等）"""
    sections = []
    # 提取第六章（格式）
    if "第六章" in tender_text or "投标文件有关格式" in tender_text:
        idx = tender_text.find("第六章")
        if idx == -1:
            idx = tender_text.find("投标文件有关格式")
        if idx != -1:
            sections.append(tender_text[idx:idx+15000])

    # 提取第五章（评分）
    if "第五章" in tender_text or "评标方法与程序" in tender_text:
        idx = tender_text.find("第五章")
        if idx == -1:
            idx = tender_text.find("评标方法与程序")
        if idx != -1:
            sections.append(tender_text[idx:idx+10000])

    # 提取第四章（需求）
    if "第四章" in tender_text or "招标需求" in tender_text:
        idx = tender_text.find("第四章")
        if idx == -1:
            idx = tender_text.find("招标需求")
        if idx != -1:
            sections.append(tender_text[idx:idx+15000])

    # 提取第一章（投标邀请/资格）
    if "第一章" in tender_text or "投标邀请" in tender_text:
        idx = tender_text.find("第一章")
        if idx == -1:
            idx = tender_text.find("投标邀请")
        if idx != -1:
            sections.append(tender_text[idx:idx+5000])

    return "\n\n=== 章节分隔 ===\n\n".join(sections)


def extract_bid_toc(bid_text: str) -> str:
    """提取投标文件的目录和关键信息"""
    # 提取前50页内容（目录和商务部分）
    pages = bid_text.split("=== PAGE ")
    toc_pages = []
    for p in pages:
        try:
            page_num = int(p.split("===")[0].strip())
            if page_num <= 80:
                toc_pages.append(p)
        except ValueError:
            toc_pages.append(p)

    return "\n".join(toc_pages)


if __name__ == "__main__":
    # 简单测试
    llm = LLM()
    print(llm.chat("你是一个助手", "你好，请简单介绍一下自己"))
