from app.models.system import SysMenu, SysElement, SysRolePermission
from app.extensions import db
from sqlalchemy.exc import SQLAlchemyError


class PermissionService:

    @staticmethod
    def get_permission_tree():
        """
        获取完整的权限树（菜单嵌套菜单 + 菜单包含元素）
        供前端权限配置页面展示
        """
        # 1. 获取所有菜单 (按 parent_id 和 sort_order 排序，保证父子处理顺序)
        menus = SysMenu.query.order_by(SysMenu.parent_id, SysMenu.sort_order).all()
        # 2. 获取所有元素
        elements = SysElement.query.all()

        # --- 核心逻辑：构建树形结构 ---

        # 3. 创建一个 lookup 字典，方便通过 ID 查找菜单节点
        # 同时将 SQLAlchemy 对象转为字典，方便后续操作
        menu_map = {}
        for m in menus:
            m_dict = m.to_dict()
            m_dict['children'] = []  # 初始化 children
            menu_map[m.id] = m_dict

        # 4. 创建 code 到 id 的映射，用于把 element 挂载到 menu 上
        # 因为 SysElement 关联的是 menu_code，而不是 menu_id
        code_to_id = {m.code: m.id for m in menus}

        # 5. 将元素 (Elements) 挂载到对应的菜单 (Menu) 下
        for el in elements:
            # 找到该元素所属菜单的 ID
            parent_menu_id = code_to_id.get(el.menu_code)
            if parent_menu_id and parent_menu_id in menu_map:
                el_dict = el.to_dict()
                # 标记类型为 element，前端 transformData 需要用到
                el_dict['type'] = 'element'
                menu_map[parent_menu_id]['children'].append(el_dict)

        # 6. 将子菜单挂载到父菜单下，并构建最终的树
        tree_data = []
        for m in menus:
            current_node = menu_map[m.id]

            if m.parent_id == 0 or m.parent_id is None:
                # 如果是顶级菜单，直接放入结果集
                tree_data.append(current_node)
            else:
                # 如果是子菜单，找到它的父级，把它塞进父级的 children 里
                if m.parent_id in menu_map:
                    menu_map[m.parent_id]['children'].append(current_node)
                else:
                    # 如果找不到父级（比如父级被删了），为了防止数据丢失，暂时作为顶级显示
                    tree_data.append(current_node)

        return tree_data

    @staticmethod
    def get_role_permissions(role_code):
        """获取指定角色拥有的所有权限Code"""
        try:
            # === 新增逻辑：超级管理员上帝模式 ===
            if role_code == 'SUPER_ADMIN':
                # 直接获取所有菜单和元素，无视配置表
                all_menus = [m.code for m in SysMenu.query.all()]
                all_elements = [e.code for e in SysElement.query.all()]
                return {
                    'menus': all_menus,
                    'elements': all_elements
                }
            # =================================

            perms = SysRolePermission.query.filter_by(role_code=role_code).all()

            menu_codes = []
            element_codes = []

            for p in perms:
                # 这里假设你的数据库存的是 target_code
                if p.type == 'menu':
                    menu_codes.append(p.target_code)
                else:
                    element_codes.append(p.target_code)

            # 前端 handleRoleSelect 会合并这两个数组，所以分开返回没问题
            return {
                'menus': menu_codes,
                'elements': element_codes
            }
        except Exception as e:
            # 记录日志或处理错误
            print(f"Error fetching role permissions: {e}")
            return {'menus': [], 'elements': []}

    @staticmethod
    def assign_permissions(role_code, permissions):
        """
        保存角色的权限
        permissions: 前端传来的 list，混合了 menu_code 和 element_code
        """
        if not role_code:
            raise ValueError("角色代码不能为空")

        session = db.session
        try:
            # 1. 开启事务 (Flask-SQLAlchemy 自动管理，但明确逻辑更好)

            # 2. 删除该角色旧的所有权限
            SysRolePermission.query.filter_by(role_code=role_code).delete()

            # 3. 准备新数据
            if permissions:
                # 3.1 去重
                unique_codes = set(permissions)

                # 3.2 预加载所有 Menu Code，用于区分是 Menu 还是 Element
                # 这一步很重要，因为 SysRolePermission 表需要 type 字段
                all_menu_codes = {res[0] for res in session.query(SysMenu.code).all()}

                new_records = []
                for code in unique_codes:
                    if not code: continue

                    # 判断类型：如果 code 存在于菜单表中，就是 menu，否则就是 element
                    p_type = 'menu' if code in all_menu_codes else 'element'

                    new_records.append(SysRolePermission(
                        role_code=role_code,
                        target_code=code,
                        type=p_type
                    ))

                # 3.3 批量插入
                if new_records:
                    session.add_all(new_records)

            # 4. 提交
            session.commit()
            return True

        except SQLAlchemyError as e:
            session.rollback()
            raise e
        except Exception as e:
            session.rollback()
            raise e
