package com.zt.plat.module.qms.framework.datapermission;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.zt.plat.framework.common.enums.UserTypeEnum;
import com.zt.plat.framework.common.util.collection.CollectionUtils;
import com.zt.plat.framework.common.util.json.JsonUtils;
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
import com.zt.plat.framework.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
//import com.zt.plat.module.qms.core.aspect.annotation.QmsPermission;
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
import com.zt.plat.module.qms.core.aspect.annotation.QmsPermission;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static java.util.Collections.singletonList;


/*
* QMS权限处理
* 1、只能查看自己创建的数据
* 2、部门领导可查看本部门数据
* 3、模块管理员可查看本模块数据
*
* */
@Component
@Slf4j
public class QMSMultiDataPermissionHandler implements MultiDataPermissionHandler {

    @Autowired private PermissionCommonApi permissionApi;
    /**
     * LoginUser 的 Context 缓存 Key
     */
    protected static final String QMS_PERMISSION_CONTEXT_KEY = "QMS_DATA_PERMISSION";
    static final Expression EXPRESSION_NULL = new NullValue();
    public static final String SYSTEM_USERS = "system_users";
    private static final String SYSTEM_USER_DEPT = "system_user_dept";
    private static final String DEPT_COLUMN_NAME = "dept_id";
    private static final String USER_COLUMN_NAME = "user_id";

    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {

        log.info("QMSMultiDataPermissionHandler - table={}, Expression={}, mappedStatementId={}",
                table.getName(), where, mappedStatementId);

        // 判断是否应该对该表应用权限（只处理主表）
        boolean applyPermission = shouldApplyPermission(table, mappedStatementId);
        if (!applyPermission) {
            return null;
        }
        //获取注解
        if(!QMSPermissionContextHolder.shouldExecute())
            return null;
        try{
            Expression expression = buildExpression(table, where);
            log.info("QMSMultiDataPermissionHandler 权限表达式：{}", expression);
            return expression;
        }catch (Exception e){
            log.error("QMSMultiDataPermissionHandler： Expression={}",  where);
        }finally {
            QMSPermissionContextHolder.clear();
        }
        return EXPRESSION_NULL;
    }

    /**
     * 判断是否应该对当前表应用权限控制（只针对主表）
     * 通过解析 mappedStatementId 找到对应的 Mapper 接口和实体类，然后判断当前表是否是主表
     *
     */
    private boolean shouldApplyPermission(Table table, String mappedStatementId) {
        try{
            // mappedStatementId 格式：com.zt.plat.module.qms.resource.material.dal.mapper.MaterialBatchMapper.selectPageWithPdtInfo
            int lastDotIndex = mappedStatementId.lastIndexOf('.');
            if (lastDotIndex == -1) {
                return true;
            }

            String mapperClassName = mappedStatementId.substring(0, lastDotIndex);
            Class<?> mapperClass = Class.forName(mapperClassName);
            // 获取方法上的@QmsPermission 注解
            Method[] methods = mapperClass.getMethods();
            boolean applyPermission = false;
            for (Method method : methods) {
                if (method.isAnnotationPresent(QmsPermission.class)) {
                    // 获取主表的表名（通过实体类）
                    String mainTableName = getMainTableName(mapperClass);
                    String currentTableName = MyBatisUtils.getTableName(table);
                    Alias tableAlias = table.getAlias();
                    String aliasName = tableAlias != null ? tableAlias.getName() : null;
                    // 只处理主表，且别名为 t 的表（避免自关联或重复应用）
                    if (mainTableName != null && mainTableName.equalsIgnoreCase(currentTableName)) {
                        // 如果有别名，检查别名是否为 "t"
                        if (aliasName != null) {
                            applyPermission = "t".equals(aliasName);
                        } else {
                            applyPermission = true;
                        }
                    }
                }
            }
            return applyPermission;
        } catch (Exception e) {
            log.warn("QMSMultiDataPermissionHandler 解析 mappedStatementId 失败：{}", mappedStatementId, e);
        }

        return false;
    }

    /**
     * 获取主表的表名
     * 通过 Mapper 接口的泛型参数找到对应的实体类，然后从@TableName 注解中获取表名
     *
     */
    private String getMainTableName(Class<?> mapperClass) {
        try{
            // 1. 获取泛型接口列表
            Type[] genericInterfaces = mapperClass.getGenericInterfaces();
            if (genericInterfaces.length == 0) return null;
            // 2. 遍历泛型接口
            for (Type type : genericInterfaces) {
                // 3. 检查是否是参数化类型
                if (!(type instanceof ParameterizedType parameterizedType)) continue;
                // 4. 获取泛型参数数组
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments.length == 0) continue;
                // 5. 获取第一个泛型参数（实体类）
                Type firstTypeArg = actualTypeArguments[0];
                if (!(firstTypeArg instanceof Class<?> entityClass)) continue;
                // 6. 检查是否有 @TableName 注解
                com.baomidou.mybatisplus.annotation.TableName tableNameAnnotation =
                        entityClass.getAnnotation(com.baomidou.mybatisplus.annotation.TableName.class);

                if (tableNameAnnotation == null) continue;

                // 7. 返回表名
                return tableNameAnnotation.value();
            }
            // 8. 未找到符合条件的表名
            log.debug("[getMainTableName] {} 未找到带@TableName 的实体类", mapperClass.getSimpleName());
            return null;

        } catch (Exception e) {
            log.warn("[getMainTableName] 获取主表名失败：{}", mapperClass.getName(), e);
            return null;
        }
    }
    /*
    * 构建权限sql*/
    private Expression buildExpression(Table table, Expression where) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) {
            return EXPRESSION_NULL;
        }
        //管理员用户，不进行权限处理
        if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {
            return null;
        }
        String deptIdCol = QMSPermissionContextHolder.getDeptIdColumn();                        //部门id列
        String userIdCol = QMSPermissionContextHolder.getUserIdColumn();                        //人员id列
        Boolean self = QMSPermissionContextHolder.getSelf();
        String deptDataRoleCodes = QMSPermissionContextHolder.getDeptDataRoleCode();             //部门数据权限角色。具有该角色，然后按角色的权限属性判断。从而获得人员允许查看的部门数据
        String moduleDataRoleCodes = QMSPermissionContextHolder.getModuleDataRoleCodes();         //模块数据权限角色。
        String custom = QMSPermissionContextHolder.getCustom();                                   //todo 自定义权限

        List<String> allRoleCode = new ArrayList<>();
        if(!ObjectUtil.isEmpty(deptDataRoleCodes)){
            allRoleCode.addAll(Arrays.asList(deptDataRoleCodes.split( ",")));
        }
        if(!ObjectUtil.isEmpty(moduleDataRoleCodes)){
            allRoleCode.addAll(Arrays.asList(moduleDataRoleCodes.split( ",")));
        }
        String roleCodes = CollUtil.join(allRoleCode, ",");
        String tableName = MyBatisUtils.getTableName(table);
        Alias tableAlias = table.getAlias();
        DeptDataPermissionRespDTO qmsDataPermission = loginUser.getContext(QMS_PERMISSION_CONTEXT_KEY, DeptDataPermissionRespDTO.class);
        if(qmsDataPermission == null){
            try{
                qmsDataPermission = permissionApi.getDeptDataPermissionWithRoleCodes(loginUser.getId(), roleCodes).getCheckedData();
            }catch (Exception e){
                log.error("[getExpression][LoginUser({}) 获取角色权限为 null]", JsonUtils.toJsonString(loginUser));
                log.error("", e);
            }finally {
                DeptContextHolder.clearRoleCodeList();
            }

            if (qmsDataPermission == null) {
                log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
                throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限", loginUser.getId(), tableName, tableAlias.getName()));
            }
            // 添加到上下文中，避免重复计算
            loginUser.setContext(QMS_PERMISSION_CONTEXT_KEY, qmsDataPermission);
        }

        Long ctxDeptId = DeptContextHolder.getDeptId();
        // 计算有效的部门与自查标记：当存在上下文部门且未被忽略时，强制仅使用该部门，以避免默认全量或空权限分支
        Set<Long> effectiveDeptIds = qmsDataPermission.getDeptIds();
        Boolean effectiveSelf = qmsDataPermission.getSelf();
        if (!DeptContextHolder.shouldIgnore() && ctxDeptId != null && ctxDeptId > 0L) {
            effectiveDeptIds = CollUtil.newHashSet(ctxDeptId);
        }

        //如果可以查看所有数据，直接跳过
        if (qmsDataPermission.getAll()) {
            return null;
        }

        // 情况一：仅当不存在上下文部门时，且 ALL 可查看全部，才无需拼接条件；若存在上下文部门则仍需基于该部门过滤
        if (ctxDeptId == null && qmsDataPermission.getAll()) {
            return null;
        }

        // 情况二：仅在有效部门集合为空且不可查看自己时，才认为无权限；若上下文提供部门，则跳过该兜底
        // TODO 这个逻辑暂时先注释，好像是存在用户角色时，不会设置部门
//        if (CollUtil.isEmpty(effectiveDeptIds) && Boolean.FALSE.equals(effectiveSelf)) {
//            return new EqualsTo(null, null); // WHERE null = null，可以保证返回的数据为空
//        }

        // 情况三，拼接 Dept 和 Company User 的条件，最后组合
        Expression deptExpression = buildDeptExpression(tableName, deptIdCol, tableAlias, effectiveDeptIds);
        // 只有当 userIdCol 不为空且 self 为 true 时，才构建用户表达式
        Expression userExpression = null;
        if (StrUtil.isNotEmpty(userIdCol) && Boolean.TRUE.equals(self)) {
            userExpression = buildUserExpression(tableName, userIdCol, tableAlias, effectiveSelf, loginUser.getId());
        }
        if (deptExpression == null && userExpression == null) {
            // TODO：获得不到条件的时候，暂时不抛出异常，而是不返回数据
            log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
                    JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(qmsDataPermission));
//            throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空",
//                    loginUser.getId(), tableName, tableAlias.getName()));
            return null;
        }
        if (deptExpression == null) {
            return userExpression;
        }
        if (userExpression == null) {
            return deptExpression;
        }
        // 目前，如果有指定部门 + 可查看自己，采用 OR 条件。即，WHERE (dept_id IN ? OR user_id = ?)
        return new ParenthesedExpressionList<>(new OrExpression(deptExpression, userExpression));
    }

    private Expression buildDeptExpression(String tableName, String columnName, Alias tableAlias, Set<Long> deptIds) {
        // 特殊处理：system_users 表没有 dept_id 字段,已经迁移到了 user_dept 表
        if (SYSTEM_USERS.equals(tableName)) {
            // system_users 走 exists 子查询 user_dept
            if (CollUtil.isEmpty(deptIds)) {
                return null;
            }
            // 构造 exists (select 1 from user_dept where user_dept.user_id = system_users.id and user_dept.dept_id in (...))
            PlainSelect plainSelect = new PlainSelect();
            plainSelect.setSelectItems(singletonList(new SelectItem<>(new LongValue(1))));
            Table userDept = new Table(SYSTEM_USER_DEPT);
            // 使用 user 表别名避免语法错误
            Table user = new Table(tableAlias == null ? tableName : tableAlias.getName());
            plainSelect.setFromItem(userDept);
            // where user_dept.user_id = system_users.id and user_dept.dept_id in (...)
            Column userDeptUserIdCol = new Column(userDept, USER_COLUMN_NAME);
            Column systemUsersIdCol = new Column(user, "id");
            EqualsTo userIdEquals = new EqualsTo(userDeptUserIdCol, systemUsersIdCol);
            Column userDeptDeptIdCol = new Column(userDept, DEPT_COLUMN_NAME);
            InExpression deptIn = new InExpression(userDeptDeptIdCol, new ParenthesedExpressionList<>(new ExpressionList<>(CollectionUtils.convertList(deptIds, LongValue::new))));
            Expression whereExp = new AndExpression(userIdEquals, deptIn);
            plainSelect.setWhere(whereExp);
            //
            ParenthesedSelect parenthesedSelect = new ParenthesedSelect();
            parenthesedSelect.setSelect(plainSelect);
            // 构建 exists 表达式
            ExistsExpression existsExpr = new ExistsExpression();
            existsExpr.setRightExpression(parenthesedSelect);
            return existsExpr;
        }
        if (StrUtil.isEmpty(columnName)) {
            return null;
        }
        // 如果为空，则无条件
        if (CollUtil.isEmpty(deptIds)) {
            return null;
        }
        /*// 拼接条件
        return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
                // Parenthesis 的目的，是提供 (1,2,3) 的 () 左右括号
                new ParenthesedExpressionList(
                        new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));*/
        // 构建：(dept_id IS NULL OR dept_id IN (?, ?, ?))
        // 含义：部门 ID 为空的数据允许所有人查看，部门 ID 不为空的数据只允许指定部门查看
        Column column = MyBatisUtils.buildColumn(tableName, tableAlias, columnName);

        // 构建 IS NULL 条件（允许部门 ID 为空的数据）
        IsNullExpression isNullExpr = new IsNullExpression(column);

        // 构建 IN 条件（限制部门 ID 不为空的数据）
        InExpression inExpression = new InExpression(column,
                new ParenthesedExpressionList<>(
                        new ExpressionList<>(CollectionUtils.convertList(deptIds, LongValue::new))));

        // 组合：IS NULL OR IN (...)
        OrExpression orExpression = new OrExpression(isNullExpr, inExpression);
        return new ParenthesedExpressionList<>(orExpression);
    }

    private Expression buildUserExpression(String tableName, String columnName, Alias tableAlias, Boolean self, Long userId) {
        // 如果不查看自己，则无需作为条件
        if (Boolean.FALSE.equals(self)) {
            return null;
        }
        if (StrUtil.isEmpty(columnName)) {
            return null;
        }
        // 拼接条件
        return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
    }

    /*
    * */
    private DeptDataPermissionRespDTO getQmsDataPermission(){
        return null;
    }

}
