package cn.iocoder.foodnexus.module.customerpermission.core.rule.customer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils;
import cn.iocoder.foodnexus.framework.common.util.json.JsonUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRule;
import cn.iocoder.foodnexus.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
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.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Set;

/**
 * @author : yanghao
 * create at:  2025/9/4  11:14
 * @description:
 */
@Slf4j
@Component
public class CustomerVisiblePermissionRule implements CustomerPermissionRule {

    /**
     * LoginUser 的 Context 缓存 Key
     */
    protected static final String CONTEXT_KEY = CustomerVisiblePermissionRule.class.getSimpleName();

    @Autowired
    private ErpCustomerApi customerApi;

    @Autowired
    private InquireCustomerApi inquireCustomerApi;

    public static final String PRODUCT_ID_COLUMN = "id";

    /**
     * 根据表名和别名，生成对应的 WHERE / OR 过滤条件
     *
     * @param tableName  表名
     * @param tableAlias 别名，可能为空
     * @return 过滤条件 Expression 表达式
     */
    @Override
    public Expression getExpression(String tableName, Alias tableAlias) {
        // 只有有登陆用户的情况下，才进行数据权限的处理
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) {
            return null;
        }
        // 只有客户类型的用户，才进行数据权限的处理
        if (!UserSystemEnum.CUSTOMER.equals(UserSystemEnum.getByKey(MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_USER_SYSTEM)))) {
            return null;
        }

        // 获得数据权限
        CustomerVisibleProductRespDTO customerVisibleDto = loginUser.getContext(CONTEXT_KEY, CustomerVisibleProductRespDTO.class);
        // 从上下文中拿不到，则调用逻辑进行获取
        if (customerVisibleDto == null) {
            Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
            customerVisibleDto = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
            if (customerVisibleDto == null || CommonUtil.isEmpty(customerVisibleDto.getItems())) {
                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(CONTEXT_KEY, customerVisibleDto);
        }

        Set<Long> productIds = CommonUtil.listConvertSet(customerVisibleDto.getItems(), CustomerVisibleProductRespDTO.CustomerProduct::getProductId);

        Expression productIdsExpression = buildDeptExpression(tableName,tableAlias, productIds);

        return new ParenthesedExpressionList(productIdsExpression);
    }

    private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> productIds) {
        // 如果为空，则无条件
        if (CollUtil.isEmpty(productIds)) {
            return null;
        }
        // 拼接条件
        return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, PRODUCT_ID_COLUMN),
                // Parenthesis 的目的，是提供 (1,2,3) 的 () 左右括号
                new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(productIds, LongValue::new))));
    }
}
