"""
图片管理服务层
处理图片分类和图片信息的业务逻辑
"""
import logging
import uuid
import os
import json
from typing import Optional, List, Dict, Any, Tuple
from datetime import datetime
from app.base.async_mysql_connection import get_db_connection
from app.base.minio_connection import get_minio_manager
from app.services.task_service import task_service

logger = logging.getLogger(__name__)

class ImageService:
    """图片管理服务类"""
    
    def __init__(self):
        """初始化服务"""
        self.minio_manager = get_minio_manager()

    async def get_categories(self) -> List[Dict[str, Any]]:
        """获取所有图片分类并构建树形结构"""
        conn = get_db_connection()
        if not conn:
            return []
        
        cursor = conn.cursor()
        try:
            sql = "SELECT * FROM t_image_category ORDER BY created_time DESC"
            cursor.execute(sql)
            categories = cursor.fetchall()
            
            # 构建树形结构
            category_dict = {str(cat['id']): {**cat, 'children': []} for cat in categories}
            root_nodes = []
            
            for cat_id, cat_node in category_dict.items():
                parent_id = str(cat_node['parent_id'])
                if parent_id == '0':
                    root_nodes.append(cat_node)
                elif parent_id in category_dict:
                    category_dict[parent_id]['children'].append(cat_node)
                else:
                    # 如果父节点找不到，也作为根节点（防御性编程）
                    root_nodes.append(cat_node)
            
            return root_nodes
        except Exception as e:
            logger.exception(f"获取图片分类失败: {e}")
            return []
        finally:
            cursor.close()
            conn.close()

    async def add_category(self, data: Dict[str, Any], user_id: str) -> Tuple[bool, str, Optional[str]]:
        """新增图片分类"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败", None
        
        cursor = conn.cursor()
        try:
            category_id = str(uuid.uuid4())
            parent_id = data.get('parent_id', '0')
            type_name = data.get('type_name')
            remark = data.get('remark')
            
            if not type_name:
                return False, "分类名称不能为空", None
            
            sql = """
                INSERT INTO t_image_category (
                    id, type_name, parent_id, remark, 
                    created_by, updated_by, created_time, updated_time
                ) VALUES (%s, %s, %s, %s, %s, %s, NOW(), NOW())
            """
            cursor.execute(sql, (category_id, type_name, parent_id, remark, user_id, user_id))
            conn.commit()
            return True, "新增成功", category_id
        except Exception as e:
            logger.exception(f"新增图片分类失败: {e}")
            conn.rollback()
            return False, f"新增失败: {str(e)}", None
        finally:
            cursor.close()
            conn.close()

    async def edit_category(self, category_id: str, data: Dict[str, Any], user_id: str) -> Tuple[bool, str]:
        """编辑图片分类"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败"
        
        cursor = conn.cursor()
        try:
            type_name = data.get('type_name')
            remark = data.get('remark')
            
            if not type_name:
                return False, "分类名称不能为空"
            
            sql = """
                UPDATE t_image_category 
                SET type_name = %s, remark = %s, updated_by = %s, updated_time = NOW()
                WHERE id = %s
            """
            cursor.execute(sql, (type_name, remark, user_id, category_id))
            conn.commit()
            return True, "编辑成功"
        except Exception as e:
            logger.exception(f"编辑图片分类失败: {e}")
            conn.rollback()
            return False, f"编辑失败: {str(e)}"
        finally:
            cursor.close()
            conn.close()

    async def delete_category(self, category_id: str) -> Tuple[bool, str]:
        """删除图片分类"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败"
        
        cursor = conn.cursor()
        try:
            # 检查是否有子分类
            cursor.execute("SELECT COUNT(*) as count FROM t_image_category WHERE parent_id = %s", (category_id,))
            if cursor.fetchone()['count'] > 0:
                return False, "该分类下有子分类，不能直接删除"
            
            # 检查是否有图片
            cursor.execute("SELECT COUNT(*) as count FROM t_image_info WHERE image_type = %s", (category_id,))
            if cursor.fetchone()['count'] > 0:
                return False, "该分类下有图片，不能直接删除"
            
            cursor.execute("DELETE FROM t_image_category WHERE id = %s", (category_id,))
            conn.commit()
            return True, "删除成功"
        except Exception as e:
            logger.exception(f"删除图片分类失败: {e}")
            conn.rollback()
            return False, f"删除失败: {str(e)}"
        finally:
            cursor.close()
            conn.close()

    async def get_image_list(
        self, 
        category_id: Optional[str] = None, 
        page: int = 1, 
        page_size: int = 10
    ) -> Dict[str, Any]:
        """获取图片列表，支持递归获取子分类图片"""
        conn = get_db_connection()
        if not conn:
            return {"total": 0, "list": []}
        
        cursor = conn.cursor()
        try:
            where_clauses = []
            params = []
            
            if category_id and category_id != '0':
                # 获取该分类及其所有子分类的 ID
                all_category_ids = await self._get_all_child_category_ids(category_id)
                all_category_ids.append(category_id)
                
                placeholders = ', '.join(['%s'] * len(all_category_ids))
                where_clauses.append(f"image_type IN ({placeholders})")
                params.extend(all_category_ids)
            
            where_sql = ""
            if where_clauses:
                where_sql = "WHERE " + " AND ".join(where_clauses)
            
            # 获取总数
            count_sql = f"SELECT COUNT(*) as total FROM t_image_info {where_sql}"
            cursor.execute(count_sql, tuple(params))
            total = cursor.fetchone()['total']
            
            # 获取列表
            offset = (page - 1) * page_size
            list_sql = f"""
                SELECT i.*, c.type_name as category_name, u.username as creator_name
                FROM t_image_info i
                LEFT JOIN t_image_category c ON i.image_type = c.id
                LEFT JOIN t_sys_user u ON i.created_by = u.id
                {where_sql}
                ORDER BY i.created_time DESC
                LIMIT %s OFFSET %s
            """
            cursor.execute(list_sql, tuple(params + [page_size, offset]))
            images = cursor.fetchall()
            
            # 处理 URL 转换
            for item in images:
                if item.get('image_url'):
                    item['image_url'] = self.minio_manager.get_full_url(item['image_url'])
            
            return {
                "total": total,
                "list": images,
                "page": page,
                "page_size": page_size
            }
        except Exception as e:
            logger.exception(f"获取图片列表失败: {e}")
            return {"total": 0, "list": []}
        finally:
            cursor.close()
            conn.close()

    async def _get_all_child_category_ids(self, parent_id: str) -> List[str]:
        """递归获取所有子分类 ID"""
        conn = get_db_connection()
        if not conn:
            return []
            
        cursor = conn.cursor()
        try:
            # 获取所有分类，在内存中构建树结构以获取所有子 ID
            # 这样比多次递归查询数据库效率更高
            sql = "SELECT id, parent_id FROM t_image_category"
            cursor.execute(sql)
            all_categories = cursor.fetchall()
            
            child_ids = []
            
            def find_children(pid: str):
                for cat in all_categories:
                    if str(cat['parent_id']) == str(pid):
                        cid = str(cat['id'])
                        child_ids.append(cid)
                        find_children(cid)
            
            find_children(parent_id)
            return child_ids
        except Exception as e:
            logger.error(f"获取子分类 ID 失败: {e}")
            return []
        finally:
            cursor.close()
            conn.close()

    async def add_image(self, data: Dict[str, Any], user_id: str) -> Tuple[bool, str]:
        """保存图片信息"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败"
        
        cursor = conn.cursor()
        try:
            image_id = str(uuid.uuid4())
            image_name = data.get('image_name')
            # 处理 URL 存储（转为相对路径）
            image_url = self.minio_manager.get_relative_path(data.get('image_url'))
            image_type = data.get('image_type')
            description = data.get('description')
            
            if not image_name or not image_url or not image_type:
                return False, "缺少必要参数"
            
            sql = """
                INSERT INTO t_image_info (
                    id, image_name, image_url, image_type, description,
                    created_by, updated_by, created_time, updated_time, whether_to_task
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, NOW(), NOW(), 0)
            """
            cursor.execute(sql, (image_id, image_name, image_url, image_type, description, user_id, user_id))
            
            # 添加到任务管理中心 (类型为 image)
            try:
                await task_service.add_task(image_id, 'image')
            except Exception as task_err:
                logger.error(f"添加图片 {image_name} 到任务中心失败: {task_err}")
                
            conn.commit()
            return True, "保存成功"
        except Exception as e:
            logger.exception(f"保存图片信息失败: {e}")
            conn.rollback()
            return False, f"保存失败: {str(e)}"
        finally:
            cursor.close()
            conn.close()

    async def delete_image(self, image_id: str) -> Tuple[bool, str]:
        """删除图片"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败"
        
        cursor = conn.cursor()
        try:
            cursor.execute("DELETE FROM t_image_info WHERE id = %s", (image_id,))
            
            # 同步从任务管理中心删除
            try:
                await task_service.delete_task(image_id)
            except Exception as task_err:
                logger.error(f"从任务中心删除图片 {image_id} 失败: {task_err}")

            conn.commit()
            return True, "删除成功"
        except Exception as e:
            logger.exception(f"删除图片失败: {e}")
            conn.rollback()
            return False, f"删除失败: {str(e)}"
        finally:
            cursor.close()
            conn.close()

    async def batch_add_to_task(self, image_ids: List[str], username: str, project_name: str, tags: List[str] = None) -> Tuple[bool, str]:
        """批量将图片加入任务中心 (单表化)"""
        conn = get_db_connection()
        if not conn:
            return False, "数据库连接失败"
        
        cursor = conn.cursor()
        try:
            if not image_ids:
                return False, "未指定要加入任务的图片 ID"
            
            # 0. 获取或生成项目 UUID
            project_id = None
            cursor.execute("SELECT project_id FROM t_task_management WHERE project_name = %s LIMIT 1", (project_name,))
            existing_project = cursor.fetchone()
            if existing_project:
                project_id = existing_project['project_id']
            else:
                project_id = str(uuid.uuid4())
            
            # 处理标签
            tag_str = json.dumps(tags, ensure_ascii=False) if tags else None
            
            # 1. 待处理图片 ID
            ids_to_add = image_ids
            
            # 2. 更新 whether_to_task 状态
            add_placeholders = ', '.join(['%s'] * len(ids_to_add))
            sql = f"UPDATE t_image_info SET whether_to_task = 1, updated_by = %s, updated_time = NOW() WHERE id IN ({add_placeholders})"
            cursor.execute(sql, (username, *ids_to_add))
            
            # 3. 确保这些图片都在 t_task_management 表中 (单表逻辑)
            for img_id in ids_to_add:
                try:
                    # 获取业务元数据
                    metadata_dict = {}
                    try:
                        # 排除内部系统字段
                        EXCLUDE_FIELDS = {
                            'id', 'created_time', 'updated_time', 'created_by', 'updated_by',
                            'image_url', 'is_deleted'
                        }
                        
                        cursor.execute("SELECT * FROM t_image_info WHERE id = %s", (img_id,))
                        img_info = cursor.fetchone()
                        if img_info:
                            for k, v in img_info.items():
                                if v is not None and v != '' and k not in EXCLUDE_FIELDS:
                                    metadata_dict[k] = v
                        
                        # 递归格式化时间
                        metadata_dict = task_service._serialize_datetime(metadata_dict)
                    except Exception as meta_err:
                        logger.warning(f"获取图片 {img_id} 元数据失败: {meta_err}")

                    await task_service.add_task(
                        business_id=img_id, 
                        task_type='image', 
                        project_id=project_id,
                        project_name=project_name,
                        tag=tag_str,
                        metadata=json.dumps(metadata_dict, ensure_ascii=False) if metadata_dict else None
                    )
                except Exception as e:
                    logger.error(f"确保图片 {img_id} 在任务中心记录失败: {e}")

            conn.commit()

            # 4. 自动推送至外部标注平台
            push_success, push_msg = await task_service.send_to_external_platform(project_id)
            
            msg = f"成功将 {len(ids_to_add)} 张图片加入项目: {project_name}"
            if push_success:
                msg += f" (已推送: {push_msg})"
            else:
                msg += f" (推送失败: {push_msg})"

            return True, msg
        except Exception as e:
            logger.exception("批量加入任务失败")
            if conn:
                conn.rollback()
            return False, f"操作失败: {str(e)}"
        finally:
            if cursor:
                cursor.close()
            if conn:
                conn.close()

    async def get_upload_url(self, filename: str, content_type: str, prefix: str = None) -> Tuple[bool, str, Dict[str, Any]]:
        """获取 MinIO 预签名上传 URL"""
        try:
            data = self.minio_manager.get_upload_url(filename, content_type, prefix=prefix)
            return True, "成功获取上传链接", data
        except Exception as e:
            logger.exception("生成上传链接失败")
            return False, f"生成上传链接失败: {str(e)}", {}
