#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
User Service - 用户服务层
"""
from io import BytesIO
from typing import Tuple, Dict, Any, Optional, List
from datetime import datetime

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.base_service import BaseService
from core.user.model import User
from core.user.schema import UserCreate, UserUpdate
from utils.security import hash_password as _hash_password, verify_password as _verify_password


class UserService(BaseService[User, UserCreate, UserUpdate]):
    """
    用户服务层
    继承BaseService，自动获得增删改查功能
    """
    
    model = User
    
    # Excel导入导出配置
    excel_columns = {
        "username": "用户名",
        "name": "姓名",
        "email": "邮箱",
        "mobile": "手机号",
        "gender": "性别",
        "user_type": "用户类型",
        "user_status": "用户状态",
    }
    excel_sheet_name = "用户列表"
    
    # 使用 generate_field_metadata 自定义字段元数据
    # 只需指定敏感字段、可脱敏字段和隐藏字段即可
    from app.field_metadata_generator import generate_field_metadata
    FIELD_METADATA = generate_field_metadata(
        User,
        sensitive_fields=['name', 'email', 'mobile', 'password'],
        maskable_fields=['name', 'email', 'mobile'],
        hidden_fields=['password'],
        field_labels={
            'username': '用户名',
            'name': '姓名',
            'email': '邮箱',
            'mobile': '手机号',
            'password': '密码',
            'gender': '性别',
            'avatar': '头像',
            'user_type': '用户类型',
            'user_status': '用户状态',
        }
    )
    
    @classmethod
    def hash_password(cls, password: str) -> str:
        """加密密码"""
        return _hash_password(password)
    
    @classmethod
    def verify_password(cls, plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return _verify_password(plain_password, hashed_password)
    
    @classmethod
    async def get_user_role_ids(cls, db: AsyncSession, user_id: str) -> List[str]:
        """
        获取用户的所有角色ID
        
        :param db: 数据库会话
        :param user_id: 用户ID
        :return: 角色ID列表
        """
        from core.user.user_role_model import UserRole
        
        stmt = select(UserRole.role_id).where(
            UserRole.user_id == user_id,
            UserRole.is_deleted == False
        )
        result = await db.execute(stmt)
        role_ids = [row[0] for row in result.all()]
        
        # 如果用户没有通过关联表分配角色，尝试从旧的 role_id 字段获取
        if not role_ids:
            user = await cls.get_by_id(db, user_id)
            if user and user.role_id:
                role_ids = [user.role_id]
        
        return role_ids
    
    @classmethod
    def _export_converter(cls, item: Any) -> Dict[str, Any]:
        """导出数据转换器"""
        return {
            "username": item.username,
            "name": item.name or "",
            "email": item.email or "",
            "mobile": item.mobile or "",
            "gender": item.get_gender_display(),
            "user_type": item.get_user_type_display(),
            "user_status": item.get_user_status_display(),
        }
    
    @classmethod
    def _import_processor(cls, row: Dict[str, Any]) -> Optional[User]:
        """导入数据处理器"""
        username = row.get("username")
        if not username:
            return None
        
        # 性别映射
        gender_map = {"未知": 0, "男": 1, "女": 2}
        gender_str = row.get("gender", "未知")
        gender = gender_map.get(gender_str, 0)
        
        # 用户类型映射
        type_map = {"系统用户": 0, "普通用户": 1, "外部用户": 2}
        type_str = row.get("user_type", "普通用户")
        user_type = type_map.get(type_str, 1)
        
        # 用户状态映射
        status_map = {"禁用": 0, "正常": 1, "锁定": 2}
        status_str = row.get("user_status", "正常")
        user_status = status_map.get(status_str, 1)
        
        return User(
            username=str(username),
            password=cls.hash_password("123456"),  # 默认密码
            name=str(row.get("name") or "") or None,
            email=str(row.get("email") or "") or None,
            mobile=str(row.get("mobile") or "") or None,
            gender=gender,
            user_type=user_type,
            user_status=user_status,
        )
    
    @classmethod
    async def export_to_excel(
        cls,
        db: AsyncSession,
        data_converter: Any = None
    ) -> BytesIO:
        """导出到Excel"""
        return await super().export_to_excel(db, cls._export_converter)
    
    @classmethod
    async def import_from_excel(
        cls,
        db: AsyncSession,
        file_content: bytes,
        row_processor: Any = None
    ) -> Tuple[int, int]:
        """从Excel导入"""
        return await super().import_from_excel(db, file_content, cls._import_processor)
    
    @classmethod
    async def create(cls, db: AsyncSession, data: UserCreate) -> User:
        """
        创建用户，自动加密密码，并处理角色关联
        """
        user_data = data.model_dump()
        # 提取 role_ids
        role_ids = user_data.pop('role_ids', None)
        
        # 加密密码
        user_data["password"] = cls.hash_password('123456')
        
        db_obj = User(**user_data)
        db.add(db_obj)
        await db.flush()  # 先 flush 获取 user id
        
        # 创建用户角色关联
        if role_ids:
            from core.user.user_role_model import UserRole
            for role_id in role_ids:
                user_role = UserRole(user_id=db_obj.id, role_id=role_id)
                db.add(user_role)
        
        await db.commit()
        await db.refresh(db_obj)
        return db_obj
    
    @classmethod
    async def get_by_username(cls, db: AsyncSession, username: str) -> Optional[User]:
        """
        根据用户名获取用户
        """
        result = await db.execute(
            select(User).where(
                User.username == username,
                User.is_deleted == False  # noqa: E712
            )
        )
        return result.scalar_one_or_none()
    
    @classmethod
    async def get_by_email(cls, db: AsyncSession, email: str) -> Optional[User]:
        """
        根据邮箱获取用户
        """
        result = await db.execute(
            select(User).where(
                User.email == email,
                User.is_deleted == False  # noqa: E712
            )
        )
        return result.scalar_one_or_none()
    
    @classmethod
    async def get_by_mobile(cls, db: AsyncSession, mobile: str) -> Optional[User]:
        """
        根据手机号获取用户
        """
        result = await db.execute(
            select(User).where(
                User.mobile == mobile,
                User.is_deleted == False  # noqa: E712
            )
        )
        return result.scalar_one_or_none()
    
    @classmethod
    async def authenticate(cls, db: AsyncSession, username: str, password: str) -> Optional[User]:
        """
        用户认证
        
        :return: 认证成功返回用户，失败返回None
        """
        user = await cls.get_by_username(db, username)
        if not user:
            return None
        if not cls.verify_password(password, user.password):
            return None
        if not user.is_active_user():
            return None
        return user
    
    @classmethod
    async def change_password(
        cls,
        db: AsyncSession,
        user_id: str,
        old_password: str,
        new_password: str
    ) -> Tuple[bool, str]:
        """
        修改密码
        
        :return: (是否成功, 消息)
        """
        user = await cls.get_by_id(db, user_id)
        if not user:
            return False, "用户不存在"
        
        if not cls.verify_password(old_password, user.password):
            return False, "原密码错误"
        
        user.password = cls.hash_password(new_password)
        await db.commit()
        return True, "密码修改成功"
    
    @classmethod
    async def reset_password(
        cls,
        db: AsyncSession,
        user_id: str,
        new_password: str
    ) -> bool:
        """
        重置密码（管理员操作）
        """
        user = await cls.get_by_id(db, user_id)
        if not user:
            return False
        
        user.password = cls.hash_password(new_password)
        await db.commit()
        return True
    
    @classmethod
    async def update(cls, db: AsyncSession, record_id: str, data: UserUpdate) -> Optional[User]:
        """
        更新用户，处理角色关联
        """
        user_data = data.model_dump(exclude_unset=True)
        # 提取 role_ids
        role_ids = user_data.pop('role_ids', None)
        
        # 更新用户基本信息
        user = await cls.get_by_id(db, record_id)
        if not user:
            return None
        
        for key, value in user_data.items():
            setattr(user, key, value)
        
        # 更新用户角色关联
        if role_ids is not None:  # 只有当传递了 role_ids 时才更新
            from core.user.user_role_model import UserRole
            from sqlalchemy import delete
            
            # 删除现有角色关联
            await db.execute(
                delete(UserRole).where(UserRole.user_id == record_id)
            )
            
            # 创建新的角色关联
            for role_id in role_ids:
                user_role = UserRole(user_id=record_id, role_id=role_id)
                db.add(user_role)
        
        await db.commit()
        await db.refresh(user)
        
        # 角色变更后清除缓存，使权限立即生效
        if role_ids is not None:
            await cls._invalidate_user_permission_cache(record_id)
        
        return user
    
    @classmethod
    async def _invalidate_user_permission_cache(cls, user_id: str):
        """
        清除用户相关的权限缓存，使角色变更立即生效
        
        1. 清除 Redis 用户信息缓存（中间件下次请求会从数据库重新加载）
        2. 清除菜单路由 Redis 缓存
        3. 清除 API 权限内存缓存
        """
        # 1. 清除 Redis 用户信息缓存（角色等动态信息）
        from utils.user_info_cache import delete_cached_user_info
        await delete_cached_user_info(user_id)
        
        # 2. 清除该用户的菜单路由缓存
        from core.menu.service import menu_cache, USER_ROUTE_CACHE_PREFIX
        await menu_cache.delete_pattern(f"{USER_ROUTE_CACHE_PREFIX}{user_id}*")
        
        # 3. 清除所有角色的 API 权限 Redis 缓存（因为不知道旧角色是哪些）
        from utils.permission import clear_all_role_permission_cache
        await clear_all_role_permission_cache()
    
    @classmethod
    async def update_last_login(
        cls,
        db: AsyncSession,
        user_id: str,
        ip: Optional[str] = None,
        login_type: Optional[str] = None
    ) -> bool:
        """
        更新最后登录信息
        """
        user = await cls.get_by_id(db, user_id)
        if not user:
            return False
        
        user.last_login = datetime.now()
        if ip:
            user.last_login_ip = ip
        if login_type:
            user.last_login_type = login_type
        
        await db.commit()
        return True
    
    @classmethod
    async def update_login_info(
        cls,
        db: AsyncSession,
        user_id: str,
        ip: Optional[str] = None,
        login_type: Optional[str] = None
    ) -> bool:
        """
        更新登录信息（update_last_login的别名）
        """
        return await cls.update_last_login(db, user_id, ip, login_type)
    
    @classmethod
    async def batch_update_status(
        cls,
        db: AsyncSession,
        ids: List[str],
        user_status: int
    ) -> int:
        """
        批量更新用户状态
        
        :return: 更新的记录数
        """
        count = 0
        for user_id in ids:
            user = await cls.get_by_id(db, user_id)
            if user and not user.is_superuser:  # 超级管理员不能被修改状态
                user.user_status = user_status
                count += 1
        
        if count > 0:
            await db.commit()
        
        return count
    
    # @classmethod
    # async def batch_delete(
    #     cls,
    #     db: AsyncSession,
    #     ids: List[str],
    #     hard: bool = False
    # ) -> Tuple[int, List[str]]:
    #     """
    #     批量删除用户
    #
    #     :return: (删除成功数, 删除失败的ID列表)
    #     """
    #     success_count = 0
    #     failed_ids = []
    #
    #     for user_id in ids:
    #         user = await cls.get_by_id(db, user_id)
    #         if user:
    #             if user.can_delete():
    #                 if await cls.delete(db, user_id, hard=hard):
    #                     success_count += 1
    #                 else:
    #                     failed_ids.append(user_id)
    #             else:
    #                 failed_ids.append(user_id)
    #         else:
    #             failed_ids.append(user_id)
    #
    #     return success_count, failed_ids
    
    @classmethod
    async def get_subordinates(cls, db: AsyncSession, user_id: str) -> List[User]:
        """
        获取下属用户列表
        """
        result = await db.execute(
            select(User).where(
                User.manager_id == user_id,
                User.is_deleted == False  # noqa: E712
            )
        )
        return list(result.scalars().all())
    
    @classmethod
    async def get_top_users(cls, db: AsyncSession) -> List[User]:
        """
        获取顶层用户（无上级的用户，用于组织架构图根节点）
        """
        from sqlalchemy import or_
        result = await db.execute(
            select(User).where(
                or_(User.manager_id == None, User.manager_id == ''),  # noqa: E711
                User.is_deleted == False  # noqa: E712
            ).order_by(User.sort.asc())
        )
        return list(result.scalars().all())
    
    @classmethod
    async def get_report_chain(cls, db: AsyncSession, user_id: str) -> List[User]:
        """
        获取用户的汇报链（从当前用户一直到顶层），返回列表 [当前用户, 上级, 上上级, ..., 顶层]
        """
        chain = []
        current_id = user_id
        visited = set()
        while current_id and current_id not in visited:
            visited.add(current_id)
            user = await cls.get_by_id(db, record_id=current_id)
            if not user:
                break
            chain.append(user)
            current_id = user.manager_id
        return chain

    @classmethod
    async def get_subordinate_count(cls, db: AsyncSession, user_id: str) -> int:
        """
        获取下属数量
        """
        from sqlalchemy import func as sa_func
        result = await db.execute(
            select(sa_func.count(User.id)).where(
                User.manager_id == user_id,
                User.is_deleted == False  # noqa: E712
            )
        )
        return result.scalar() or 0

    @classmethod
    async def get_by_dept(cls, db: AsyncSession, dept_id: str) -> List[User]:
        """
        获取部门下的用户列表
        """
        result = await db.execute(
            select(User).where(
                User.dept_id == dept_id,
                User.is_deleted == False  # noqa: E712
            )
        )
        return list(result.scalars().all())
