import os
import dashscope
from natsort import natsorted
import time
from var import TONGYI_API_KEY, OCR_MODEL
import logging
import traceback
from openai import OpenAI

# 获取当前模块的logger
logger = logging.getLogger(__name__)


def call_tongyi_ocr(system_prompt, ocr_result, image_url):
    """
    调用通义千问OCR接口识别图片中的表格内容

    Args:
        system_prompt (str): 系统提示词
        ocr_result (str): OCR识别结果的描述
        image_url (str): 图片URL地址

    Returns:
        tuple: (识别结果内容, 输入token数, 输出token数) 或 (None, 0, 0) 当调用失败时
    """
    logger.info("开始调用通义千问OCR接口")
    logger.debug("OCR系统提示词长度: %d 字符", len(system_prompt))
    logger.debug("图片URL: %s", image_url)

    messages = [
        {"role": "system", "content": system_prompt},
        {
            "role": "user",
            "content": [
                {"image": image_url},
                {"text": ocr_result}
            ]
        }
    ]

    logger.debug("构建完成多模态消息，准备调用API")

    try:
        start_time = time.time()
        logger.info("发送OCR请求到通义千问API，模型: %s", OCR_MODEL)

        response = dashscope.MultiModalConversation.call(
            api_key=TONGYI_API_KEY,
            model=OCR_MODEL,
            messages=messages,
            # response_format={"type": "json_object"},
            temperature=0.1
        )

        api_call_time = time.time() - start_time
        logger.info("通义千问API调用完成，耗时: %.2f秒", api_call_time)

        if response.status_code != 200:
            logger.error(
                "通义千问OCR接口调用失败，状态码: %d, 错误信息: %s",
                response.status_code,
                response.text
            )
            return None, 0, 0

        logger.debug("API响应状态码: %d", response.status_code)

        # 提取响应内容
        if (hasattr(response, 'output') and
                hasattr(response.output, 'choices') and
                len(response.output.choices) > 0 and
                'message' in response.output.choices[0] and
                'content' in response.output.choices[0].message and
                len(response.output.choices[0].message['content']) > 0):

            content = response.output.choices[0].message['content'][0]['text']
            input_tokens = getattr(response.usage, 'input_tokens', 0)
            output_tokens = getattr(response.usage, 'output_tokens', 0)

            logger.info(
                "OCR识别成功，输入token: %d, 输出token: %d, 响应内容长度: %d 字符",
                input_tokens, output_tokens, len(content)
            )
            logger.debug("OCR识别结果前100字符: %s", content[:100] + "..." if len(content) > 100 else content)

            return content, input_tokens, output_tokens
        else:
            logger.error("API响应格式异常，无法提取有效内容")
            logger.debug("完整响应对象: %s", response)
            return None, 0, 0

    except Exception as e:
        logger.error("调用通义千问OCR接口时发生异常: %s", str(e), exc_info=True)
        logger.error("异常堆栈信息: %s", traceback.format_exc())
        return None, 0, 0


def call_tongyi_ocr_stream(system_prompt, ocr_result, image_url):
    """
    调用通义千问OCR接口识别图片中的表格内容（流式版本）

    优化点：
    1. 分离内容流式返回和token统计功能
    2. 简化yield逻辑，专注于内容流
    3. 使用上下文管理器确保资源清理
    4. 添加更详细的错误处理

    Args:
        system_prompt (str): 系统提示词
        ocr_result (str): OCR识别结果的描述
        image_url (str): 图片URL地址

    Yields:
        str: 内容片段（流式返回）
        tuple: (输入token数, 输出token数) 在流结束时返回
    """
    logger.info("开始调用通义千问OCR接口（流式模式）")
    logger.debug("OCR系统提示词长度: %d 字符", len(system_prompt))
    logger.debug("图片URL: %s", image_url)
    logger.debug("OCR结果描述长度: %d 字符", len(ocr_result))

    # 构建符合OpenAI兼容模式的多模态消息格式
    messages = [
        {"role": "system", "content": system_prompt},
        {
            "role": "user",
            "content": [
                {"type": "image_url", "image_url": {"url": image_url}},
                {"type": "text", "text": ocr_result}
            ]
        }
    ]

    try:
        # 1. 初始化OpenAI客户端
        client = OpenAI(
            api_key=TONGYI_API_KEY,
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
        )

        start_time = time.time()
        logger.info("发送OCR流式请求到通义千问API，模型: %s", OCR_MODEL)

        # 2. 发起流式请求
        with client.chat.completions.create(
                model=OCR_MODEL,
                messages=messages,
                stream=True,
                stream_options={"include_usage": True},
                temperature=0.1
        ) as stream:
            api_call_time = time.time() - start_time
            logger.info("通义千问流式API连接建立，耗时: %.2f秒", api_call_time)

            content_parts = []  # 用于暂存响应片段
            input_tokens = 0
            output_tokens = 0

            logger.debug("开始处理流式响应")

            # 3. 处理流式响应
            for chunk in stream:
                if chunk.choices and chunk.choices[0].delta.content is not None:
                    content = chunk.choices[0].delta.content
                    content_parts.append(content)

                    # 实时yield每个内容片段
                    logger.debug("收到内容片段: %s", content[:50] + "..." if len(content) > 50 else content)
                    yield content

                elif chunk.usage:
                    # 更新token使用量
                    input_tokens = getattr(chunk.usage, 'prompt_tokens', 0)
                    output_tokens = getattr(chunk.usage, 'completion_tokens', 0)
                    total_tokens = getattr(chunk.usage, 'total_tokens', 0)

                    logger.info(
                        "收到用量统计，输入token: %d, 输出token: %d, 总计: %d",
                        input_tokens, output_tokens, total_tokens
                    )

            # 构建完整响应内容
            full_content = "".join(content_parts)

            logger.info(
                "OCR流式识别完成，总输入token: %d, 总输出token: %d, 总内容长度: %d 字符",
                input_tokens, output_tokens, len(full_content)
            )
            logger.debug("OCR识别完整结果: %s", full_content[:200] + "..." if len(full_content) > 200 else full_content)

            # 最后返回token统计
            yield input_tokens, output_tokens

    except Exception as e:
        logger.error("调用通义千问OCR流式接口时发生异常: %s", str(e), exc_info=True)
        logger.error("异常堆栈信息: %s", traceback.format_exc())
        # 返回空token统计表示错误
        yield 0, 0


def llm_ocr(image_path):
    system_prompt = f"""
    你是一个专业的OCR工具，识别图片里表格的内容：
    1. 检测出图片里完整的表格，如果表格不完整，则直接忽略
    2. 表格数据前31行表示一年中每一天的数据，竖向记录，一列表示一个月，识别时也要竖向识别，按月份返回数据。切记这是真实数据，只有一月、三月、五月、七月、八月、十月、十二月，有31天。二月只有28或者29天，其余月份只有30天
    3. 第32行记录的是平均数，若均值不存在，则对应值记录为None

    数据返回格式为:{{"1月": {{"data": [0.1, 0.2], "avg": 0.3}}, "2月": {{...}}}}，每个月份数据的长度，要与这个月的天数相符。如果某个月份存在数据缺失，则用null进行填充
    """
    for filename in natsorted(os.listdir(image_path)):
        if filename not in ["102.jpg", "103.jpg"]:
            continue
        start_time = time.time()
        print(f"处理图片: {filename}", end='')
        if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
            continue
        file_prefix = filename.rsplit('.', 1)[0]
        image_url = f"https://questions-1309102452.cos.ap-nanjing.myqcloud.com/images/ruiwen/{filename}"
        res = call_tongyi_ocr(system_prompt, "识别图中的表格", image_url)
        with open(os.path.join(image_path, f"{file_prefix}_processed.json"), 'w', encoding='utf-8') as f:
            f.write(res)
        print(f"，耗时: {time.time() - start_time:.2f} 秒")
        time.sleep(3)


if __name__ == "__main__":
    image_path = r"D:\project\hydrological_data_analyse/input"
    llm_ocr(image_path)