"""
OCR服务模块
---------
提供OCR识别服务，协调OCR流程。
"""

from typing import Dict, List, Optional, Tuple, Union, Any, Callable
import os

from app.config.settings import ConfigManager
from app.core.utils.log_utils import get_logger
from app.core.ocr.table_ocr import OCRProcessor

logger = get_logger(__name__)

class OCRService:
    """
    OCR识别服务：协调OCR流程
    """
    
    def __init__(self, config: Optional[ConfigManager] = None):
        """
        初始化OCR服务
        
        Args:
            config: 配置管理器，如果为None则创建新的
        """
        logger.info("初始化OCRService")
        self.config = config or ConfigManager()
        
        # 创建OCR处理器
        self.ocr_processor = OCRProcessor(self.config)
        
        logger.info("OCRService初始化完成")
    
    def get_unprocessed_images(self) -> List[str]:
        """
        获取待处理的图片列表
        
        Returns:
            待处理图片路径列表
        """
        return self.ocr_processor.get_unprocessed_images()
    
    def process_image(self, image_path: str) -> Optional[str]:
        """
        处理单个图片文件
        
        Args:
            image_path: 图片文件路径
            
        Returns:
            生成的Excel文件路径，如果处理失败则返回None
        """
        try:
            # 检查文件是否存在
            if not os.path.exists(image_path):
                logger.error(f"文件不存在: {image_path}")
                return None
                
            # 检查文件类型
            if not self._is_valid_image(image_path):
                logger.error(f"不支持的文件类型: {image_path}")
                return None

            # 不再做 xlsx 已存在就跳过的判断（xlsx 可能被业务层重命名过），
            # 跳过逻辑交给 OCRProcessor 内部的 record_manager.is_processed(image_path)
            # —— 由它读 processed_files.json（业务层会同步更新 xlsx 路径）
                
            # 执行OCR识别
            result = self.ocr_processor.process_image(image_path)
            if not result:
                logger.error(f"OCR识别失败: {image_path}")
                return None

            # 生成Excel文件
            excel_file = self._generate_excel(result, image_path)
            if not excel_file:
                logger.error(f"生成Excel文件失败: {image_path}")
                return None

            # 写 meta.json（从 ocr_client 再做一次通用文字识别获取抬头/日期）
            self._write_meta_json(excel_file, {}, image_path)
                
            logger.info(f"处理完成: {image_path} -> {excel_file}")
            return excel_file
            
        except Exception as e:
            logger.error(f"处理图片时发生错误: {e}", exc_info=True)
            return None
    
    def process_images_batch(self, batch_size: int = None, max_workers: int = None, progress_cb: Optional[Callable[[int], None]] = None) -> Tuple[int, int]:
        """
        批量处理图片
        
        Args:
            batch_size: 批处理大小
            max_workers: 最大线程数
            
        Returns:
            (总处理数, 成功处理数)元组
        """
        logger.info(f"OCRService开始批量处理图片, batch_size={batch_size}, max_workers={max_workers}")
        return self.ocr_processor.process_images_batch(batch_size, max_workers, progress_cb)
    
    # 添加batch_process作为process_images_batch的别名，确保兼容性
    def batch_process(self, batch_size: int = None, max_workers: int = None, progress_cb: Optional[Callable[[int], None]] = None) -> Tuple[int, int]:
        """
        批量处理图片（别名方法，与process_images_batch功能相同）
        
        Args:
            batch_size: 批处理大小
            max_workers: 最大线程数
            
        Returns:
            (总处理数, 成功处理数)元组
        """
        logger.info(f"OCRService.batch_process被调用，转发到process_images_batch")
        return self.process_images_batch(batch_size, max_workers, progress_cb)
    
    def validate_image(self, image_path: str) -> bool:
        """
        验证图片是否有效
        
        Args:
            image_path: 图片路径
            
        Returns:
            图片是否有效
        """
        return self.ocr_processor.validate_image(image_path)
    
    def _is_valid_image(self, image_path: str) -> bool:
        """
        检查文件是否为有效的图片格式
        
        Args:
            image_path: 图片文件路径
            
        Returns:
            是否为有效图片格式
        """
        return self.validate_image(image_path)
    
    def _get_excel_path(self, image_path: str) -> str:
        """
        根据图片路径生成对应的Excel文件路径
        
        Args:
            image_path: 图片文件路径
            
        Returns:
            Excel文件路径
        """
        # 获取文件名（不含扩展名）
        base_name = os.path.splitext(os.path.basename(image_path))[0]
        # 生成Excel文件路径
        output_dir = self.config.get_path('Paths', 'output_folder', fallback='data/output', create=True) if hasattr(self.config, 'get_path') else os.path.abspath('data/output')
        excel_path = os.path.join(output_dir, f"{base_name}.xlsx")
        return excel_path
    
    def _generate_excel(self, ocr_result: dict, image_path: str) -> Optional[str]:
        """
        根据OCR结果生成Excel文件
        
        Args:
            ocr_result: OCR识别结果
            image_path: 原始图片路径
            
        Returns:
            生成的Excel文件路径，失败返回None
        """
        try:
            excel_path = self._get_excel_path(image_path)

            # 确保输出目录存在
            os.makedirs(os.path.dirname(excel_path), exist_ok=True)

            # 调用OCR处理器的Excel生成功能
            if hasattr(self.ocr_processor, 'generate_excel'):
                success = self.ocr_processor.generate_excel(ocr_result, excel_path)
                if success:
                    return excel_path
            else:
                # OCR 处理器（table_ocr.OCRProcessor）已直接生成 xlsx
                if os.path.exists(excel_path):
                    return excel_path
            
            return None

        except Exception as e:
            logger.error(f"生成Excel文件时发生错误: {e}", exc_info=True)
            return None

    def _write_meta_json(self, excel_path: str, ocr_result: dict, image_path: str) -> None:
        """从 OCR 结果中抽取原始文本，写入与 Excel 同名的 .meta.json。

        用途：供元信息提取器（OrderMetadataExtractor）使用，无需重新 OCR。
        失败不影响主流程。
        """
        try:
            import json
            import base64
            from pathlib import Path

            base = Path(excel_path)
            meta_path = base.with_suffix('.meta.json')

            # 0) 优先：调百度通用票务识别（/general_ocr），精准捕获表头（含手写抬头/日期）
            general_text = ''
            general_lines = []
            try:
                client = getattr(self.ocr_processor, 'ocr_client', None)
                if client is None:
                    try:
                        self.ocr_processor._ensure_ocr_client()
                    except Exception:
                        pass
                    client = getattr(self.ocr_processor, 'ocr_client', None)
                
                # 关键：在进行第二次 OCR (票务识别) 前稍微等待，避免触发 QPS 限制
                import time
                time.sleep(0.5)

                # 优先使用票务识别 (Ticket OCR)
                if client and hasattr(client, 'recognize_ticket') and image_path and os.path.exists(str(image_path)):
                    logger.info("使用通用票务识别捕获表头元数据...")
                    words = client.recognize_ticket(str(image_path))
                    if words:
                        general_lines = [(w.get('words') or '').strip() for w in words if (w.get('words') or '').strip()]
                        general_text = '\n'.join(general_lines)
                        logger.info(f"票务识别获取 {len(general_lines)} 行文字")
                
                # 如果票务识别无结果或不可用，则兜底使用高精度文字识别
                if not general_lines and client and hasattr(client, 'recognize_general') and image_path and os.path.exists(str(image_path)):
                    logger.info("尝试高精度文字识别作为兜底...")
                    words = client.recognize_general(str(image_path))
                    if words:
                        def _top(w):
                            loc = w.get('location') or {}
                            try:
                                return float(loc.get('top', 0))
                            except (TypeError, ValueError):
                                return 0.0
                        words_sorted = sorted(words, key=_top)
                        general_lines = [(w.get('words') or '').strip() for w in words_sorted if (w.get('words') or '').strip()]
                        general_text = '\n'.join(general_lines)
                        logger.info(f"通用高精度识别获取 {len(general_lines)} 行文字")
            except Exception as e:
                logger.warning(f"获取元数据识别失败（不影响主流程）: {e}")

            # 1) 表格识别 header/body 拼表内文字（已在前面逻辑处理）
            ocr_text = ''
            ocr_rows = []
            for key in ('text', 'raw_text', 'ocr_text', 'content'):
                v = ocr_result.get(key)
                if isinstance(v, str) and v.strip():
                    ocr_text = v
                    break

            # 2) 如果没有，尝试从 tables_result 拼
            if not ocr_text and isinstance(ocr_result, dict):
                tables = ocr_result.get('tables_result') or []
                if isinstance(tables, list):
                    lines = []
                    for t in tables:
                        if not isinstance(t, dict):
                            continue
                        # 关键：从 header / body / footer 都提取，header 含供应商/日期抬头
                        for region in ('header', 'body', 'footer'):
                            region_data = t.get(region) or []
                            if not isinstance(region_data, list):
                                continue
                            for cell in region_data:
                                if not isinstance(cell, dict):
                                    continue
                                words = cell.get('words') or cell.get('word') or ''
                                if not words:
                                    continue
                                row = cell.get('row') or []
                                col = cell.get('column') or []
                                # 按行归一化拼成文本
                                if isinstance(row, list) and row:
                                    row_key = f"r{row[0]}"
                                else:
                                    row_key = f"r{len(lines)}"
                                # 简化：直接每行一个 cell
                                lines.append(str(words).strip())
                                ocr_rows.append([str(words)])
                    if not ocr_text and lines:
                        ocr_text = '\n'.join(lines)

            # 3) 兜底：从 Excel 文件读 cell 拼
            if not ocr_text:
                try:
                    import xlrd
                    rb = xlrd.open_workbook(str(excel_path))
                    ws = rb.sheet_by_index(0)
                    lines = []
                    for r in range(ws.nrows):
                        row_vals = [str(ws.cell_value(r, c)) for c in range(ws.ncols)]
                        ocr_rows.append(row_vals)
                        lines.append(' '.join(row_vals))
                    ocr_text = '\n'.join(lines)
                except Exception as e:
                    logger.debug(f"从 xlsx 读 OCR 文本失败: {e}")

            payload = {
                'excel_path': str(excel_path),
                'image_path': str(image_path),
                'ocr_text': ocr_text,
                'ocr_rows': ocr_rows,
                'general_text': general_text,
                'general_lines': general_lines,
                'created_at': __import__('datetime').datetime.now().isoformat(timespec='seconds'),
            }
            meta_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding='utf-8')
            logger.debug(f"meta.json 已写入: {meta_path}")
        except Exception as e:
            logger.warning(f"写 meta.json 失败（不影响主流程）: {e}")
