import os
import json
from typing import Dict, Any, List

class LLMService:
    """大语言模型服务，支持本地Ollama或OpenAI API"""
    
    def __init__(self):
        self.llm_type = self._detect_llm_type()
        self._initialize_llm()
    
    def _detect_llm_type(self) -> str:
        """检测可用的LLM类型"""
        # 优先使用 DeepSeek（如果已配置）
        if os.getenv("DEEPSEEK_API_KEY") and os.getenv("USE_DEEPSEEK_LLM", "true").lower() == "true":
            return "deepseek"
        # 检查 OpenAI
        elif os.getenv("OPENAI_API_KEY"):
            return "openai"
        # Coze（如果已配置）
        elif os.getenv("COZE_API_KEY") and os.getenv("COZE_WORKFLOW_ID"):
            return "coze"
        elif os.getenv("OLLAMA_HOST") or self._check_ollama_available():
            return "ollama"
        else:
            return "mock"
    
    def _check_ollama_available(self) -> bool:
        """检查Ollama是否可用"""
        try:
            import requests
            response = requests.get("http://localhost:11434/api/tags", timeout=2)
            return response.status_code == 200
        except:
            return False
    
    def _initialize_llm(self):
        """初始化LLM客户端"""
        if self.llm_type == "deepseek":
            try:
                self.deepseek_api_key = os.getenv("DEEPSEEK_API_KEY")
                self.deepseek_api_url = os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com") + "/v1/chat/completions"
                self.model = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
                print(f"✓ 使用 DeepSeek API (模型: {self.model})")
            except Exception as e:
                print(f"⚠ DeepSeek 初始化失败: {e}")
                self.llm_type = "mock"
        
        elif self.llm_type == "openai":
            try:
                from openai import OpenAI
                
                # 支持自定义API端点
                api_key = os.getenv("OPENAI_API_KEY")
                api_base = os.getenv("OPENAI_API_BASE")
                
                if api_base:
                    self.client = OpenAI(api_key=api_key, base_url=api_base)
                else:
                    self.client = OpenAI(api_key=api_key)
                
                self.model = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
                print(f"✓ 使用 OpenAI API (模型: {self.model})")
            except Exception as e:
                print(f"⚠ OpenAI 初始化失败: {e}")
                self.llm_type = "mock"
        
        elif self.llm_type == "ollama":
            try:
                import requests
                self.ollama_host = os.getenv("OLLAMA_HOST", "http://localhost:11434")
                # 默认使用已安装的 qwen2.5:7b 模型，如需更换可通过 OLLAMA_MODEL 环境变量覆盖
                self.model = os.getenv("OLLAMA_MODEL", "qwen2.5:7b")
                print(f"✓ 使用 Ollama (模型: {self.model})")
            except Exception as e:
                print(f"⚠ Ollama 初始化失败: {e}")
                self.llm_type = "mock"
        
        elif self.llm_type == "coze":
            try:
                # Coze 工作流调用所需配置，通过环境变量提供
                self.coze_api_url = os.getenv("COZE_API_URL", "https://api.coze.cn/v1/workflow/run")
                self.coze_api_key = os.getenv("COZE_API_KEY")
                self.coze_workflow_id = os.getenv("COZE_WORKFLOW_ID")

                if not self.coze_api_key or not self.coze_workflow_id:
                    raise ValueError("COZE_API_KEY 或 COZE_WORKFLOW_ID 未配置")

                print("✓ 使用 Coze 工作流作为LLM")
            except Exception as e:
                print(f"⚠ Coze 初始化失败: {e}")
                self.llm_type = "mock"
        
        if self.llm_type == "mock":
            print("✓ 使用模拟LLM模式（用于演示）")
    
    def analyze_single_report(self, report_text: str) -> Dict[str, Any]:
        """分析单个报告"""
        prompt = f"""请分析以下医疗报告，提取关键信息：

{report_text}

请提供：
1. 摘要
2. 关键发现
3. 异常指标
4. 风险评估
5. 建议

以JSON格式返回结果。
"""
        
        if self.llm_type == "deepseek":
            return self._call_deepseek(prompt)
        elif self.llm_type == "openai":
            return self._call_openai(prompt)
        elif self.llm_type == "ollama":
            return self._call_ollama(prompt)
        elif self.llm_type == "coze":
            # 对于 Coze，直接将原始报告文本传给工作流，由工作流内部负责解析与生成结构化结果
            print(f"    → 准备调用 Coze 工作流...")
            coze_input = [{
                "filename": "single_report",
                "text": report_text,
            }]
            result = self._call_coze(coze_input)  # 单个报告也作为数组传入
            print(f"    ← Coze 调用返回")
            return result
        else:
            return self._mock_analysis(report_text)
    
    def analyze_multiple_reports(self, report_texts: List[str]) -> Dict[str, Any]:
        """
        分析多个报告（Coze专用）
        report_texts: 报告文本的数组，每个元素是一个PDF的文本
        """
        if self.llm_type == "coze":
            print(f"    → 准备调用 Coze 工作流（传入 {len(report_texts)} 个报告）...")
            result = self._call_coze(report_texts)
            print(f"    ← Coze 调用返回")
            return result
        else:
            # 其他LLM类型合并文本后调用
            combined = "\n\n".join(report_texts)
            return self.analyze_single_report(combined)
    
    def _call_openai(self, prompt: str) -> Dict[str, Any]:
        """调用OpenAI API"""
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": "你是一位专业的医疗报告分析助手。"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7,
                max_tokens=2000
            )
            
            content = response.choices[0].message.content
            return self._parse_llm_response(content)
            
        except Exception as e:
            return {
                "error": f"OpenAI API 调用失败: {str(e)}",
                "summary": "分析失败",
                "key_findings": [],
                "abnormal_items": [],
                "risk_assessment": "无法评估",
                "recommendations": []
            }
    
    def _call_deepseek(self, prompt: str) -> Dict[str, Any]:
        """调用 DeepSeek API"""
        try:
            import requests
            
            headers = {
                "Authorization": f"Bearer {self.deepseek_api_key}",
                "Content-Type": "application/json"
            }
            
            data = {
                "model": self.model,
                "messages": [
                    {"role": "system", "content": "你是一位专业的医疗报告分析助手。请以JSON格式返回分析结果。"},
                    {"role": "user", "content": prompt}
                ],
                "temperature": 0.3,
                "max_tokens": 4000
            }
            
            response = requests.post(
                self.deepseek_api_url,
                headers=headers,
                json=data,
                timeout=120
            )
            
            if response.status_code == 200:
                content = response.json()["choices"][0]["message"]["content"]
                return self._parse_llm_response(content)
            else:
                raise Exception(f"DeepSeek 返回错误: {response.status_code} - {response.text}")
                
        except Exception as e:
            return {
                "error": f"DeepSeek API 调用失败: {str(e)}",
                "summary": "分析失败",
                "key_findings": [],
                "abnormal_items": [],
                "risk_assessment": "无法评估",
                "recommendations": []
            }
    
    def _call_coze(self, report_texts: List[str]) -> Dict[str, Any]:
        """
        调用 Coze 工作流 API（流式模式），对医疗报告进行分析
        report_texts: 报告文本数组，每个元素是一个PDF的文本
        """
        try:
            import time
            from cozepy import Coze, TokenAuth, COZE_CN_BASE_URL, WorkflowEventType

            api_key = getattr(self, "coze_api_key", os.getenv("COZE_API_KEY"))
            workflow_id = getattr(self, "coze_workflow_id", os.getenv("COZE_WORKFLOW_ID"))
            max_retries = int(os.getenv("COZE_MAX_RETRIES", "3"))

            if not api_key or not workflow_id:
                raise ValueError("未配置 Coze API 所需的 COZE_API_KEY 或 COZE_WORKFLOW_ID")

            print(f"    → 调用 Coze 工作流（流式模式）...")
            print(f"    → Workflow ID: {workflow_id}")
            print(f"    → 数组元素个数: {len(report_texts)}")
            total_chars = 0
            for item in report_texts:
                if isinstance(item, str):
                    total_chars += len(item)
                elif isinstance(item, dict):
                    text_value = item.get("text")
                    if isinstance(text_value, str):
                        total_chars += len(text_value)
            print(f"    → 总文本长度: {total_chars} 字符")
            print(f"    → 请求发送时间: {time.strftime('%H:%M:%S')}")
            
            # 初始化 Coze 客户端
            coze = Coze(auth=TokenAuth(token=api_key), base_url=COZE_CN_BASE_URL)
            
            # 添加请求开始时间
            import time as time_module
            start = time_module.time()
            
            last_error = None
            for attempt in range(max_retries):
                try:
                    if attempt > 0:
                        print(f"    → 重试 {attempt}/{max_retries - 1}...")
                    
                    # 调用流式接口
                    stream = coze.workflows.runs.stream(
                        workflow_id=workflow_id,
                        parameters={"input": report_texts}
                    )
                    
                    print(f"    ✓ 已连接到流式接口，等待执行...")
                    
                    # 处理事件流
                    content_result = None
                    event_count = 0
                    for event in stream:
                        event_count += 1
                        print(f"    [事件 {event_count}] 类型: {event.event}")
                        
                        if event.event == WorkflowEventType.MESSAGE:
                            # 打印进度信息
                            if hasattr(event, 'message') and event.message:
                                msg = event.message
                                node_type = getattr(msg, 'node_type', None)
                                node_title = getattr(msg, 'node_title', None)
                                node_is_finish = getattr(msg, 'node_is_finish', None)
                                content = getattr(msg, 'content', None)
                                
                                print(f"      节点标题: {node_title}")
                                print(f"      节点类型: {node_type}")
                                print(f"      是否完成: {node_is_finish}")
                                print(f"      内容长度: {len(content) if content else 0}")
                                
                                if node_title:
                                    print(f"    ⏳ 执行节点: {node_title} (类型: {node_type})")
                                
                                # 检查是否为结束节点（使用 node_title 判断）
                                if node_title == "End" and node_is_finish and content:
                                    print(f"    ✓ 工作流执行完成，获取到结果")
                                    content_result = content
                                    break
                        
                        elif event.event == WorkflowEventType.ERROR:
                            error_msg = str(event.error) if hasattr(event, 'error') else "Unknown error"
                            print(f"    ✗ 错误事件: {error_msg}")
                            raise Exception(f"工作流执行错误: {error_msg}")
                        
                        elif event.event == WorkflowEventType.INTERRUPT:
                            print(f"    ⚠️  工作流需要交互，暂不支持")
                            raise Exception("工作流需要人工交互，当前不支持")
                    
                    if not content_result:
                        raise Exception("未获取到工作流执行结果")
                    
                    elapsed = time_module.time() - start
                    print(f"    → 收到完整结果 (耗时: {elapsed:.1f}秒)")
                    print(f"    → 结果数据: {content_result[:200]}...")
                    
                    # 解析 content 字段（通常包含 JSON 格式的输出）
                    # content 格式示例: {"output":"```json\n{...}\n```"}
                    if isinstance(content_result, str):
                        # 尝试解析为 JSON
                        try:
                            content_json = json.loads(content_result)
                            output = content_json.get("output", content_result)
                        except json.JSONDecodeError:
                            output = content_result
                        
                        # 如果 output 包含 markdown 格式的 JSON，提取出来
                        if isinstance(output, str) and "```json" in output:
                            import re
                            json_match = re.search(r'```json\s*\n(.*?)\n```', output, re.DOTALL)
                            if json_match:
                                output = json_match.group(1)
                        
                        # 尝试解析最终的 JSON
                        data = {"code": 0, "data": {"output": output}}
                    else:
                        data = {"code": 0, "data": {"output": content_result}}

                    # 参考间隔定时脚本的返回结构：{ code: 0, data: { output: ... } }
                    if isinstance(data, dict) and data.get("code") == 0:
                        raw_data = data.get("data", {})
                        if isinstance(raw_data, str):
                            try:
                                raw_data = json.loads(raw_data)
                            except json.JSONDecodeError:
                                # data.data 为字符串，直接按 LLM 文本解析
                                return self._parse_llm_response(raw_data)

                        # 期望 workflow 在 data.output 中返回结果
                        output = raw_data.get("output", raw_data)
                        
                        # 如果 output 还是字符串，再次解析
                        if isinstance(output, str):
                            try:
                                output = json.loads(output)
                                print(f"    ✓ Coze 返回的 output 需要二次解析")
                            except json.JSONDecodeError:
                                print(f"    ⚠️ output 为字符串但无法解析为JSON，尝试文本解析")
                                return self._parse_llm_response(output)

                        if isinstance(output, dict):
                            # 如果已经是结构化结果，直接补齐字段
                            print(f"    ✓ Coze 返回结构化数据")
                            print(f"    → 包含字段: {list(output.keys())}")
                            
                            result = output
                            required_fields = [
                                "summary",
                                "key_findings",
                                "abnormal_items",
                                "risk_assessment",
                                "recommendations",
                            ]
                            for field in required_fields:
                                if field not in result:
                                    result[field] = [] if field in [
                                        "key_findings",
                                        "abnormal_items",
                                        "recommendations",
                                    ] else "未提供"
                            
                            print(f"    ✓✓ Coze 工作流调用成功！")
                            return result

                        if isinstance(output, str):
                            # output 为文本，通过原有 JSON 解析逻辑处理
                            return self._parse_llm_response(output)

                        # 其它类型（列表等），转为字符串后再解析
                        return self._parse_llm_response(json.dumps(output, ensure_ascii=False))

                    # code 非 0，视为错误
                    last_error = f"Coze API 返回非0 code: {data}"
                    print(f"    ✗ Coze 返回错误: {last_error}")

                except Exception as e:  # 包含超时在内的所有请求异常
                    last_error = str(e)
                    print(f"    ✗ Coze API 调用失败: {last_error}")
                    if attempt < max_retries - 1:
                        # 简单的递增退避等待
                        wait_time = (attempt + 1) * 3
                        print(f"    → 等待 {wait_time} 秒后重试...")
                        time.sleep(wait_time)
                    else:
                        print(f"    ✗✗ 已达最大重试次数，放弃调用")
                        break

            print(f"    ✗✗ Coze 工作流调用最终失败: {last_error}")
            raise Exception(last_error or "Coze API 调用失败")

        except Exception as e:
            return {
                "error": f"Coze API 调用失败: {str(e)}",
                "summary": "分析失败",
                "key_findings": [],
                "abnormal_items": [],
                "risk_assessment": "无法评估",
                "recommendations": []
            }
    
    def _call_ollama(self, prompt: str) -> Dict[str, Any]:
        """调用Ollama API"""
        try:
            import requests
            
            response = requests.post(
                f"{self.ollama_host}/api/generate",
                json={
                    "model": self.model,
                    "prompt": prompt,
                    "stream": False
                },
                timeout=300
            )
            
            if response.status_code == 200:
                content = response.json().get("response", "")
                return self._parse_llm_response(content)
            else:
                raise Exception(f"Ollama 返回错误: {response.status_code}")
                
        except Exception as e:
            return {
                "error": f"Ollama API 调用失败: {str(e)}",
                "summary": "分析失败",
                "key_findings": [],
                "abnormal_items": [],
                "risk_assessment": "无法评估",
                "recommendations": []
            }
    
    def _parse_llm_response(self, response: str) -> Dict[str, Any]:
        """解析LLM响应"""
        try:
            # 尝试提取JSON内容
            import re
            
            # 查找JSON代码块
            json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', response, re.DOTALL)
            if json_match:
                json_str = json_match.group(1)
            else:
                # 查找裸JSON
                json_match = re.search(r'\{.*\}', response, re.DOTALL)
                if json_match:
                    json_str = json_match.group(0)
                else:
                    json_str = response
            
            result = json.loads(json_str)
            
            # 验证必需字段
            required_fields = ["summary", "key_findings", "abnormal_items", "risk_assessment", "recommendations"]
            for field in required_fields:
                if field not in result:
                    result[field] = [] if field in ["key_findings", "abnormal_items", "recommendations"] else "未提供"
            
            return result
            
        except:
            # 解析失败，返回原始文本
            return {
                "summary": "无法解析LLM响应",
                "raw_response": response,
                "key_findings": [],
                "abnormal_items": [],
                "risk_assessment": "解析失败",
                "recommendations": []
            }
    
    def _mock_analysis(self, report_text: str) -> Dict[str, Any]:
        """模拟分析结果"""
        return {
            "summary": "这是一份血常规检查报告。根据报告内容，各项指标均在正常参考范围内，未发现明显异常。",
            "key_findings": [
                "白细胞计数: 6.5×10^9/L，正常范围",
                "红细胞计数: 4.8×10^12/L，正常范围",
                "血红蛋白: 145 g/L，正常范围",
                "血小板计数: 220×10^9/L，正常范围"
            ],
            "abnormal_items": [],
            "risk_assessment": "低风险。所有检测指标均在正常范围内，未发现需要关注的异常项。建议定期体检，保持健康生活方式。",
            "recommendations": [
                "继续保持良好的生活习惯",
                "定期进行健康体检（建议每年一次）",
                "保持均衡饮食和适量运动",
                "如有不适症状，及时就医"
            ],
            "note": "这是一个模拟的分析结果。实际使用时请配置 OpenAI API 或本地 Ollama 模型。"
        }
