"""
模板服务
管理表格线条模板的创建、应用、预览和删除
"""
import json
import os
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from datetime import datetime


class TemplateService:
    """表格线条模板服务"""
    
    # 模板存储目录
    _file_path = Path(__file__).resolve()
    TEMPLATE_DIR = _file_path.parent.parent / "templates"
    TEMPLATE_SUFFIX = ".template.json"
    
    def __init__(self):
        """初始化模板服务，确保模板目录存在"""
        self.TEMPLATE_DIR.mkdir(parents=True, exist_ok=True)
    
    def _get_template_path(self, name: str) -> Path:
        """获取模板文件路径"""
        # 清理名称，移除不安全字符
        safe_name = "".join(c for c in name if c.isalnum() or c in "._- ")
        return self.TEMPLATE_DIR / f"{safe_name}{self.TEMPLATE_SUFFIX}"
    
    def create_template(
        self, 
        name: str, 
        structure: Dict,
        image_size: Dict,
        description: str = ""
    ) -> Dict:
        """
        从当前结构创建模板
        
        Args:
            name: 模板名称（用户自定义）
            structure: 当前表格结构 {horizontal_lines, vertical_lines, table_bbox, ...}
            image_size: 图片尺寸 {width, height}
            description: 模板描述
            
        Returns:
            创建的模板信息
        """
        if not name or not name.strip():
            raise ValueError("模板名称不能为空")
        
        name = name.strip()
        template_path = self._get_template_path(name)
        
        if template_path.exists():
            raise ValueError(f"模板 '{name}' 已存在")
        
        # 提取模板信息
        horizontal_lines = structure.get("horizontal_lines", [])
        vertical_lines = structure.get("vertical_lines", [])
        table_bbox = structure.get("table_bbox")
        
        if not horizontal_lines and not vertical_lines:
            raise ValueError("结构中没有线条信息")
        
        # 计算相对坐标（相对于 table_bbox，这样应用时可以直接映射到目标 bbox）
        img_width = image_size.get("width", 1)
        img_height = image_size.get("height", 1)
        
        # 如果有 table_bbox，计算相对于 bbox 的坐标；否则相对于整个图片
        if table_bbox and len(table_bbox) == 4:
            bbox_x1, bbox_y1, bbox_x2, bbox_y2 = table_bbox
            bbox_width = bbox_x2 - bbox_x1
            bbox_height = bbox_y2 - bbox_y1
            
            # 相对于 table_bbox 的坐标 (0-1)
            rel_horizontal_lines = [(y - bbox_y1) / bbox_height for y in horizontal_lines] if bbox_height > 0 else []
            rel_vertical_lines = [(x - bbox_x1) / bbox_width for x in vertical_lines] if bbox_width > 0 else []
            # table_bbox 本身相对于图片尺寸
            rel_table_bbox = [
                bbox_x1 / img_width,
                bbox_y1 / img_height,
                bbox_x2 / img_width,
                bbox_y2 / img_height,
            ]
        else:
            # 没有 table_bbox 时，相对于整个图片
            rel_horizontal_lines = [y / img_height for y in horizontal_lines]
            rel_vertical_lines = [x / img_width for x in vertical_lines]
            rel_table_bbox = None
        
        template = {
            "name": name,
            "description": description,
            "created_at": datetime.now().isoformat(),
            "source_image_size": image_size,
            # 存储绝对坐标
            "horizontal_lines": horizontal_lines,
            "vertical_lines": vertical_lines,
            "table_bbox": table_bbox,
            # 相对坐标 (0-1范围)，相对于 table_bbox，用于不同尺寸图片的适配
            "relative": {
                "horizontal_lines": rel_horizontal_lines,
                "vertical_lines": rel_vertical_lines,
                "table_bbox": rel_table_bbox
            },
            # 统计信息
            "stats": {
                "row_count": len(horizontal_lines) - 1 if len(horizontal_lines) > 1 else 0,
                "col_count": len(vertical_lines) - 1 if len(vertical_lines) > 1 else 0,
            }
        }
        
        # 保存模板
        with open(template_path, 'w', encoding='utf-8') as f:
            json.dump(template, f, ensure_ascii=False, indent=2)
        
        return {
            "name": name,
            "path": str(template_path),
            "stats": template["stats"],
            "created_at": template["created_at"]
        }
    
    def list_templates(self) -> List[Dict]:
        """
        列出所有模板
        
        Returns:
            模板列表 [{name, description, created_at, stats}, ...]
        """
        templates = []
        
        for file_path in self.TEMPLATE_DIR.glob(f"*{self.TEMPLATE_SUFFIX}"):
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    template = json.load(f)
                templates.append({
                    "name": template.get("name", file_path.stem),
                    "description": template.get("description", ""),
                    "created_at": template.get("created_at", ""),
                    "stats": template.get("stats", {}),
                    "source_image_size": template.get("source_image_size", {})
                })
            except Exception as e:
                print(f"读取模板 {file_path} 失败: {e}")
                continue
        
        # 按创建时间排序（最新在前）
        templates.sort(key=lambda x: x.get("created_at", ""), reverse=True)
        return templates
    
    def get_template(self, name: str) -> Optional[Dict]:
        """
        获取模板详情
        
        Args:
            name: 模板名称
            
        Returns:
            模板详情或 None
        """
        template_path = self._get_template_path(name)
        if not template_path.exists():
            return None
        
        with open(template_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    
    def delete_template(self, name: str) -> bool:
        """
        删除模板
        
        Args:
            name: 模板名称
            
        Returns:
            是否删除成功
        """
        template_path = self._get_template_path(name)
        if template_path.exists():
            template_path.unlink()
            return True
        return False
    
    def preview_apply(
        self, 
        template_name: str, 
        target_image_size: Dict,
        target_table_bbox: Optional[List] = None,
        mode: str = "relative"
    ) -> Dict:
        """
        预览模板应用效果（不保存）
        
        Args:
            template_name: 模板名称
            target_image_size: 目标图片尺寸 {width, height}
            target_table_bbox: 目标页面的表格边界框 [x1, y1, x2, y2]
                              如果提供，则将模板线条映射到此区域内
                              如果不提供，则使用模板的 table_bbox（缩放后）
            mode: 应用模式
                - "relative": 按相对坐标缩放（适用于不同尺寸图片）
                - "absolute": 使用绝对坐标（适用于相同尺寸图片）
                
        Returns:
            预览结构 {horizontal_lines, vertical_lines, table_bbox, ...}
        """
        template = self.get_template(template_name)
        if not template:
            raise ValueError(f"模板 '{template_name}' 不存在")
        
        target_width = target_image_size.get("width", 1)
        target_height = target_image_size.get("height", 1)
        
        # 获取模板的相对坐标信息（相对于 table_bbox 的 0-1 坐标）
        relative = template.get("relative", {})
        rel_h_lines = relative.get("horizontal_lines", [])
        rel_v_lines = relative.get("vertical_lines", [])
        rel_bbox = relative.get("table_bbox")
        
        if mode == "absolute":
            # 使用绝对坐标（直接使用模板原始值）
            horizontal_lines = template.get("horizontal_lines", []).copy()
            vertical_lines = template.get("vertical_lines", []).copy()
            table_bbox = template.get("table_bbox")
        else:
            # relative 模式
            if target_table_bbox and len(target_table_bbox) == 4:
                # 使用目标页面的 table_bbox
                bbox_x1, bbox_y1, bbox_x2, bbox_y2 = target_table_bbox
                table_bbox = list(target_table_bbox)
            elif rel_bbox and len(rel_bbox) == 4:
                # 使用模板的 table_bbox（缩放到目标图片尺寸）
                bbox_x1 = int(rel_bbox[0] * target_width)
                bbox_y1 = int(rel_bbox[1] * target_height)
                bbox_x2 = int(rel_bbox[2] * target_width)
                bbox_y2 = int(rel_bbox[3] * target_height)
                table_bbox = [bbox_x1, bbox_y1, bbox_x2, bbox_y2]
            else:
                # 没有 bbox 信息，使用整个图片
                bbox_x1, bbox_y1 = 0, 0
                bbox_x2, bbox_y2 = target_width, target_height
                table_bbox = [bbox_x1, bbox_y1, bbox_x2, bbox_y2]
            
            bbox_width = bbox_x2 - bbox_x1
            bbox_height = bbox_y2 - bbox_y1
            
            # 将模板的相对位置(0-1)映射到目标 bbox 内
            horizontal_lines = [
                int(bbox_y1 + y * bbox_height) 
                for y in rel_h_lines
            ]
            vertical_lines = [
                int(bbox_x1 + x * bbox_width) 
                for x in rel_v_lines
            ]
        
        # 强制对齐：确保第一条和最后一条线与 table_bbox 边界一致
        if table_bbox and horizontal_lines:
            if len(horizontal_lines) > 0:
                horizontal_lines[0] = table_bbox[1]  # 第一条横线 = top
            if len(horizontal_lines) > 1:
                horizontal_lines[-1] = table_bbox[3]  # 最后一条横线 = bottom
        
        if table_bbox and vertical_lines:
            if len(vertical_lines) > 0:
                vertical_lines[0] = table_bbox[0]  # 第一条竖线 = left
            if len(vertical_lines) > 1:
                vertical_lines[-1] = table_bbox[2]  # 最后一条竖线 = right
        
        # 计算行列数
        total_rows = len(horizontal_lines) - 1 if len(horizontal_lines) > 1 else 0
        total_cols = len(vertical_lines) - 1 if len(vertical_lines) > 1 else 0
        
        return {
            "horizontal_lines": horizontal_lines,
            "vertical_lines": vertical_lines,
            "table_bbox": table_bbox,
            "total_rows": total_rows,
            "total_cols": total_cols,
            "modified_h_lines": [],
            "modified_v_lines": [],
            "applied_template": template_name,
            "apply_mode": mode
        }
    
    def apply_template(
        self, 
        template_name: str, 
        target_image_size: Dict,
        target_table_bbox: Optional[List] = None,
        mode: str = "relative"
    ) -> Dict:
        """
        应用模板（返回结构，由调用者决定是否保存）
        
        与 preview_apply 相同，但标记为已确认应用
        """
        structure = self.preview_apply(template_name, target_image_size, target_table_bbox, mode)
        structure["confirmed"] = True
        return structure


# 单例
_template_service: Optional[TemplateService] = None

def get_template_service() -> TemplateService:
    """获取模板服务单例"""
    global _template_service
    if _template_service is None:
        _template_service = TemplateService()
    return _template_service
