"""
标签分类服务层
处理标签分类的业务逻辑、树形结构管理、路径维护等
"""
import logging
from typing import Optional, List, Dict, Tuple, Any
from datetime import datetime, timezone

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, and_, or_
from app.sample.models.tag_category import TagCategory
from app.sample.schemas.tag_category import (
    TagCategoryCreate, TagCategoryUpdate, TagCategoryResponse
)
from app.services.user_service import UserService

logger = logging.getLogger(__name__)


class TagCategoryService:
    """标签分类服务类 - db session由路由层注入"""
    
    def __init__(self, session: AsyncSession):
        """初始化服务，接收数据库session"""
        self.session = session
        self.user_service = UserService(session)
    
    async def create_tag_category(
        self,
        tag_data: TagCategoryCreate,
        created_by: Optional[str] = None
    ) -> TagCategoryResponse:
        """创建标签分类"""
        try:
            path = "/"
            level = 1
            
            if tag_data.parent_id != 0:
                result = await self.session.execute(
                    select(TagCategory).where(
                        and_(
                            TagCategory.id == tag_data.parent_id,
                            TagCategory.is_deleted == 0
                        )
                    )
                )
                parent = result.scalar_one_or_none()
                
                if not parent:
                    raise ValueError(f"父级分类ID {tag_data.parent_id} 不存在")
                
                path = f"{parent.path}{tag_data.parent_id}/"
                level = parent.level + 1
            
            tag_category = TagCategory(
                parent_id=tag_data.parent_id,
                name=tag_data.name,
                type=tag_data.type or "label",
                path=path,
                level=level,
                sort_no=tag_data.sort_no or 0,
                status=tag_data.status or 1,
                created_by=created_by,
                created_at=datetime.now(timezone.utc),
                updated_by=created_by,
                updated_at=datetime.now(timezone.utc)
            )
            
            self.session.add(tag_category)
            await self.session.flush()
            await self.session.refresh(tag_category)
            
            logger.info(f"创建标签分类成功: {tag_category.id} - {tag_category.name}")
            return await self._enrich_with_user_names(tag_category)
        
        except Exception as e:
            logger.error(f"创建标签分类失败: {str(e)}")
            raise
    
    async def get_tag_category_by_id(self, category_id: int) -> Optional[TagCategoryResponse]:
        """根据ID获取标签分类"""
        try:
            result = await self.session.execute(
                select(TagCategory).where(
                    and_(
                        TagCategory.id == category_id,
                        TagCategory.is_deleted == 0
                    )
                )
            )
            tag_category = result.scalar_one_or_none()
            if tag_category:
                return await self._enrich_with_user_names(tag_category)
            return None
        
        except Exception as e:
            logger.error(f"查询标签分类失败: {str(e)}")
            raise
    
    async def list_tag_categories(
        self,
        parent_id: Optional[int] = None,
        status: Optional[int] = None,
        skip: int = 0,
        limit: int = 100
    ) -> List[TagCategoryResponse]:
        """列表查询标签分类"""
        try:
            conditions = [
                TagCategory.is_deleted == 0,
                TagCategory.type == 'label'
            ]
            
            if parent_id is not None:
                conditions.append(TagCategory.parent_id == parent_id)
            
            if status is not None:
                conditions.append(TagCategory.status == status)
            
            query = select(TagCategory).where(and_(*conditions))
            query = query.order_by(TagCategory.sort_no, TagCategory.id)
            query = query.offset(skip).limit(limit)
            
            result = await self.session.execute(query)
            categories = result.scalars().all()
            
            return await self._enrich_list_with_user_names(categories, parent_id)
        
        except Exception as e:
            logger.error(f"查询标签分类列表失败: {str(e)}")
            raise
    
    async def update_tag_category(
        self,
        category_id: int,
        tag_data: TagCategoryUpdate,
        updated_by: Optional[str] = None
    ) -> TagCategoryResponse:
        """更新标签分类"""
        try:
            result = await self.session.execute(
                select(TagCategory).where(
                    and_(
                        TagCategory.id == category_id,
                        TagCategory.is_deleted == 0
                    )
                )
            )
            tag_category = result.scalar_one_or_none()
            
            if not tag_category:
                raise ValueError(f"分类ID {category_id} 不存在")
            
            path_changed = tag_data.parent_id is not None and tag_data.parent_id != tag_category.parent_id
            
            if path_changed:
                if tag_data.parent_id != 0:
                    result = await self.session.execute(
                        select(TagCategory).where(
                            and_(
                                TagCategory.id == tag_data.parent_id,
                                TagCategory.is_deleted == 0
                            )
                        )
                    )
                    parent = result.scalar_one_or_none()
                    
                    if not parent:
                        raise ValueError(f"新的父级分类ID {tag_data.parent_id} 不存在")
                    
                    if self._is_ancestor(tag_category, parent):
                        raise ValueError("不能将分类移动到其子级分类下")
                    
                    tag_category.path = f"{parent.path}{tag_data.parent_id}/"
                    tag_category.level = parent.level + 1
                else:
                    tag_category.path = "/"
                    tag_category.level = 1
                
                tag_category.parent_id = tag_data.parent_id
                
                await self._update_descendants_path(tag_category)
            
            if tag_data.name is not None:
                tag_category.name = tag_data.name
            if tag_data.type is not None:
                tag_category.type = tag_data.type
            if tag_data.sort_no is not None:
                tag_category.sort_no = tag_data.sort_no
            if tag_data.status is not None:
                tag_category.status = tag_data.status
            
            tag_category.updated_by = updated_by
            tag_category.updated_at = datetime.now(timezone.utc)
            
            await self.session.flush()
            await self.session.refresh(tag_category)
            
            logger.info(f"更新标签分类成功: {tag_category.id}")
            return await self._enrich_with_user_names(tag_category)
        
        except Exception as e:
            logger.error(f"更新标签分类失败: {str(e)}")
            raise
    
    async def delete_tag_category(
        self,
        category_id: int,
        soft_delete: bool = True
    ) -> bool:
        """删除标签分类"""
        try:
            result = await self.session.execute(
                select(TagCategory).where(TagCategory.id == category_id)
            )
            tag_category = result.scalar_one_or_none()
            
            if not tag_category:
                raise ValueError(f"分类ID {category_id} 不存在")
            
            if soft_delete:
                await self._soft_delete_with_children(category_id)
            else:
                await self.session.delete(tag_category)
            
            logger.info(f"删除标签分类成功: {category_id}")
            return True
        
        except Exception as e:
            logger.error(f"删除标签分类失败: {str(e)}")
            raise
    
    async def get_category_tree(
        self,
        root_id: int = 0,
        include_disabled: bool = False
    ) -> List[TagCategory]:
        """获取标签分类树"""
        try:
            conditions = [TagCategory.is_deleted == 0]
            if not include_disabled:
                conditions.append(TagCategory.status == 1)
            
            result = await self.session.execute(
                select(TagCategory).where(and_(*conditions))
                .order_by(TagCategory.sort_no, TagCategory.id)
            )
            all_categories = result.scalars().all()
            
            # 构建树
            tree = self._build_tree(all_categories, root_id)
            
            # 为树节点添加 parent_name 和用户名称
            await self._enrich_tree_with_names(tree, all_categories)
            
            return tree
        
        except Exception as e:
            logger.error(f"获取分类树失败: {str(e)}")
            raise
    
    @staticmethod
    def _build_tree(categories: List[TagCategory], parent_id: int = 0) -> List[TagCategory]:
        """递归构建树形结构"""
        tree = []
        for cat in categories:
            if cat.parent_id == parent_id:
                children = TagCategoryService._build_tree(categories, cat.id)
                # 直接设置 cat 的 children 属性
                cat.children = children if children else None
                tree.append(cat)
        return tree
    
    async def _enrich_tree_with_names(self, tree: List[TagCategory], all_categories: List[TagCategory]) -> None:
        """为树节点批量填充 parent_name 和用户名称"""
        # 构建 category id 到 name 的映射
        category_map = {cat.id: cat.name for cat in all_categories}
        
        # 收集所有用户ID
        user_ids = set()
        for cat in all_categories:
            if cat.created_by:
                user_ids.add(cat.created_by)
            if cat.updated_by:
                user_ids.add(cat.updated_by)
        
        # 批量查询用户信息
        user_map = {}
        for user_id in user_ids:
            try:
                user = await self.user_service.get_user_by_id(user_id)
                if user:
                    user_map[user_id] = user.username
            except Exception as e:
                logger.warning(f"查询用户 {user_id} 失败: {str(e)}")
        
        # 递归填充每个节点
        self._enrich_node_with_names(tree, category_map, user_map)
    
    def _enrich_node_with_names(self, nodes: List[TagCategory], category_map: dict, user_map: dict) -> None:
        """递归为节点填充名称"""
        for node in nodes:
            # 填充 parent_name
            if node.parent_id != 0:
                node.parent_name = category_map.get(node.parent_id)
            else:
                node.parent_name = None
            
            # 填充用户名称
            if node.created_by:
                node.created_by_name = user_map.get(node.created_by)
            if node.updated_by:
                node.updated_by_name = user_map.get(node.updated_by)
            
            # 递归处理子节点
            if hasattr(node, 'children') and node.children:
                self._enrich_node_with_names(node.children, category_map, user_map)
    
    async def get_breadcrumb(self, category_id: int) -> List[Dict[str, Any]]:
        """获取分类的面包屑路径"""
        try:
            result = await self.session.execute(
                select(TagCategory).where(
                    and_(
                        TagCategory.id == category_id,
                        TagCategory.is_deleted == 0
                    )
                )
            )
            category = result.scalar_one_or_none()
            
            if not category:
                return []
            
            path_ids = [int(x) for x in category.path.strip('/').split('/') if x]
            path_ids.append(category.id)
            
            if not path_ids:
                return []
            
            result = await self.session.execute(
                select(TagCategory).where(TagCategory.id.in_(path_ids))
                .order_by(TagCategory.level)
            )
            ancestors = result.scalars().all()
            
            return [{"id": cat.id, "name": cat.name} for cat in ancestors]
        
        except Exception as e:
            logger.error(f"获取面包屑路径失败: {str(e)}")
            raise
    
    @staticmethod
    def _is_ancestor(ancestor: TagCategory, descendant: TagCategory) -> bool:
        """检查ancestor是否是descendant的祖先"""
        path_ids = [int(x) for x in descendant.path.strip('/').split('/') if x]
        return ancestor.id in path_ids
    
    async def _update_descendants_path(self, parent: TagCategory) -> None:
        """递归更新所有子分类的path和level"""
        result = await self.session.execute(
            select(TagCategory).where(TagCategory.parent_id == parent.id)
        )
        children = result.scalars().all()
        
        for child in children:
            child.path = f"{parent.path}{parent.id}/"
            child.level = parent.level + 1
            child.updated_at = datetime.now(timezone.utc)
            
            await self._update_descendants_path(child)
    
    async def _soft_delete_with_children(self, category_id: int) -> None:
        """软删除分类及其所有子分类"""
        result = await self.session.execute(
            select(TagCategory).where(TagCategory.id == category_id)
        )
        category = result.scalar_one_or_none()
        
        if not category:
            return
        
        path_prefix = f"{category.path}{category_id}/"
        
        query = update(TagCategory).where(
            or_(
                TagCategory.id == category_id,
                TagCategory.path.like(f"{path_prefix}%")
            )
        ).values(
            is_deleted=1,
            updated_at=datetime.now(timezone.utc)
        )
        
        await self.session.execute(query)
    
    async def batch_delete_tag_categories(
        self,
        category_ids: List[int],
        soft_delete: bool = True
    ) -> Tuple[int, str]:
        """批量删除标签分类"""
        try:
            if not category_ids:
                return 0, "分类ID列表为空"
            
            success_count = 0
            
            for cat_id in category_ids:
                try:
                    result = await self.session.execute(
                        select(TagCategory).where(TagCategory.id == cat_id)
                    )
                    tag_category = result.scalar_one_or_none()
                    
                    if not tag_category:
                        continue
                    
                    if soft_delete:
                        await self._soft_delete_with_children(cat_id)
                    else:
                        await self.session.delete(tag_category)
                    
                    success_count += 1
                except Exception as e:
                    logger.warning(f"删除分类 {cat_id} 失败: {str(e)}")
            
            message = f"成功删除 {success_count}/{len(category_ids)} 条分类"
            return success_count, message
        
        except Exception as e:
            logger.error(f"批量删除分类失败: {str(e)}")
            raise
    
    async def move_tag_category(
        self,
        category_id: int,
        target_parent_id: int,
        updated_by: Optional[str] = None
    ) -> TagCategoryResponse:
        """移动分类到新的父分类"""
        update_data = TagCategoryUpdate(parent_id=target_parent_id)
        return await self.update_tag_category(category_id, update_data, updated_by)
    
    async def reorder_siblings(
        self,
        category_id: int,
        new_sort_no: int,
        updated_by: Optional[str] = None
    ) -> TagCategoryResponse:
        """重新排序分类"""
        update_data = TagCategoryUpdate(sort_no=new_sort_no)
        return await self.update_tag_category(category_id, update_data, updated_by)
    
    async def get_children_count(
        self,
        category_id: int,
        recursive: bool = False
    ) -> int:
        """获取分类的子分类数量"""
        try:
            if recursive:
                result = await self.session.execute(
                    select(TagCategory).where(
                        and_(
                            TagCategory.path.like(f"/%{category_id}/%"),
                            TagCategory.is_deleted == 0
                        )
                    )
                )
            else:
                result = await self.session.execute(
                    select(TagCategory).where(
                        and_(
                            TagCategory.parent_id == category_id,
                            TagCategory.is_deleted == 0
                        )
                    )
                )
            
            categories = result.scalars().all()
            return len(categories)
        
        except Exception as e:
            logger.error(f"获取子分类数量失败: {str(e)}")
            raise
    
    async def _enrich_with_user_names(self, category: TagCategory) -> TagCategoryResponse:
        """为单个分类对象填充用户名"""
        response = TagCategoryResponse.model_validate(category)
        
        if category.created_by:
            user = await self.user_service.get_user_by_id(category.created_by)
            response.created_by_name = user.username if user else None
        
        if category.updated_by:
            user = await self.user_service.get_user_by_id(category.updated_by)
            response.updated_by_name = user.username if user else None
        
        return response
    
    async def _enrich_list_with_user_names(self, categories: List[TagCategory], parent_id: Optional[int] = None) -> List[TagCategoryResponse]:
        """为分类列表批量填充用户名和父分类名"""
        if not categories:
            return []
        
        # 收集所有需要查询的用户ID
        user_ids = set()
        for cat in categories:
            if cat.created_by:
                user_ids.add(cat.created_by)
            if cat.updated_by:
                user_ids.add(cat.updated_by)
        
        # 批量查询用户名
        user_map = {}
        if user_ids:
            for user_id in user_ids:
                user = await self.user_service.get_user_by_id(user_id)
                if user:
                    user_map[user_id] = user.username
        
        # 如果指定了 parent_id，所有分类都有相同的父分类，只需查询一次
        parent_name = None
        if parent_id is not None and parent_id != 0:
            result = await self.session.execute(
                select(TagCategory).where(TagCategory.id == parent_id)
            )
            parent = result.scalar_one_or_none()
            if parent:
                parent_name = parent.name
        
        # 构建响应列表
        result = []
        for cat in categories:
            response = TagCategoryResponse.model_validate(cat)
            response.created_by_name = user_map.get(cat.created_by) if cat.created_by else None
            response.updated_by_name = user_map.get(cat.updated_by) if cat.updated_by else None
            # 如果指定了 parent_id，使用查询出的 parent_name；否则为 None
            response.parent_name = parent_name if parent_id is not None and parent_id != 0 else None
            result.append(response)
        
        return result
