package com.seer.teach.common.data.permission.rule.role;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.seer.teach.common.data.permission.rule.DataPermissionCondition;
import com.seer.teach.common.data.permission.rule.DataPermissionRule;
import com.seer.teach.common.entity.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import org.springframework.util.StringUtils;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 基于角色的数据权限规则：限制用户只能访问自己所属数据（如 creatorId = 当前用户ID）
 */
@AllArgsConstructor
@Slf4j
public class RoleDataPermissionRule implements DataPermissionRule {

    /**
     * 缓存 SFunction -> 属性名 的映射
     */
    private static final ConcurrentMap<SFunction<?, ?>, String> PROPERTY_NAME_CACHE = new ConcurrentHashMap<>();

    /**
     * 存储每个表对应的用户字段（如 creator_id, user_id 等）
     * key: 表名 (tableName)
     * value: 对应的数据库字段名
     */
    private final ConcurrentMap<String, String> userColumns = new ConcurrentHashMap<>();

    private DataPermissionCondition condition;

    /**
     * 所有受此规则影响的表名集合
     */
    private final Set<String> tableNames = ConcurrentHashMap.newKeySet();

    public RoleDataPermissionRule() {

    }

    @Override
    public Set<String> getTableNames() {
        return tableNames;
    }

    @Override
    public Expression getExpression(String tableName, Alias tableAlias) {
        if (!StpUtil.isLogin()) {
            log.warn("用户未登录，跳过数据权限控制，表名：{}", tableName);
            return null;
        }

        int userId = StpUtil.getLoginIdAsInt();
        return buildUserExpression(tableName, tableAlias, userId);
    }

    @Override
    public DataPermissionCondition getCondition() {
        return condition;
    }

    /**
     * 构建用户字段的等值表达式：tableAlias.columnName = userId
     */
    private Expression buildUserExpression(String tableName, Alias tableAlias, int userId) {
        String columnName = userColumns.get(tableName);
        if (StrUtil.isEmpty(columnName)) {
            log.debug("表 [{}] 未配置用户字段，跳过数据权限", tableName);
            return null;
        }

        Column column = buildColumn(tableName, tableAlias, columnName);
        return new EqualsTo(column, new LongValue(userId));
    }

    /**
     * 根据实体类和 Java 属性名注册数据权限字段
     */
    public <T extends BaseEntity> void addUserColumn(Class<T> entityClass, String propertyName) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        if (tableInfo == null) {
            log.error("无法获取实体类 {} 的 TableInfo", entityClass.getName());
            return;
        }

        tableInfo.getFieldList().stream()
                .filter(fieldInfo -> fieldInfo.getProperty().equals(propertyName))
                .findFirst()
                .ifPresent(
                        fieldInfo -> {
                            String columnName = fieldInfo.getColumn();
                            addUserColumn(tableInfo.getTableName(), columnName);
                        }
                );
    }

    /**
     * 直接根据表名和数据库字段名注册权限字段
     */
    public void addUserColumn(String tableName, String columnName) {
        if (StrUtil.isBlank(tableName) || StrUtil.isBlank(columnName)) {
            log.warn("注册失败：表名或字段名为null或空字符串");
            return;
        }
        userColumns.put(tableName, columnName);
        tableNames.add(tableName);
        log.debug("手动注册表 {} 的用户字段：{}", tableName, columnName);
    }


    public <T extends BaseEntity> void addUserColumn(Class<T> entityClass, SFunction<T, ?> methodRef) {
        if (entityClass == null || methodRef == null) {
            log.warn("addUserColumn: 实体类或方法引用为空");
            return;
        }
        String propertyName = PROPERTY_NAME_CACHE.computeIfAbsent(methodRef, ref -> {
            LambdaMeta meta = LambdaUtils.extract(ref);
            return extractPropertyName(meta.getImplMethodName());
        });

        if (StrUtil.isNotBlank(propertyName)) {
            addUserColumn(entityClass, propertyName);
        } else {
            log.error("无法从方法引用解析出属性名：{}", methodRef);
            throw new IllegalArgumentException("无效的SFunction，无法提取属性名");
        }
    }


    private String extractPropertyName(String methodName) {
        if (StrUtil.isBlank(methodName)) return null;

        if (methodName.startsWith("get") && methodName.length() > 3) {
            return StringUtils.uncapitalize(methodName.substring(3));
        } else if (methodName.startsWith("is") && methodName.length() > 2) {
            return StringUtils.uncapitalize(methodName.substring(2));
        }
        return null;
    }

    /**
     * 构造带别名的列对象
     */
    public Column buildColumn(String tableName, Alias tableAlias, String columnName) {
        String actualTableName = tableAlias != null ? tableAlias.getName() : tableName;
        return new Column(actualTableName + StringPool.DOT + columnName);
    }

    public void setCondition(DataPermissionCondition condition) {
        this.condition = condition;
    }
}