package cn.iocoder.foodnexus.module.system.aspect;

import cn.iocoder.foodnexus.framework.common.exception.ServiceException;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
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 cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.system.enums.ErrorCodeConstants.CUSTOMER_PRODUCT_CUSTOMER_ERROR;

/**
 * @author : yanghao
 * create at:  2025/9/5  11:20
 * @description: 自动set价格
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AutoSetPriceAspect {

    @Autowired
    private InquireCustomerApi inquireCustomerApi;

    @Autowired
    private ErpCustomerApi customerApi;

    // 切点：匹配所有带有 @AutoSetPrice 注解的方法
    @Pointcut("@annotation(cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice)")
    public void autoSetPricePointcut() {}


    @Around("autoSetPricePointcut() && @annotation(autoSetPrice)")
    public Object fillPrice(ProceedingJoinPoint joinPoint, AutoSetPrice autoSetPrice) throws Throwable {
        Object result = joinPoint.proceed();
        if (result == null) return null;

        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (CommonUtil.isEmpty(loginUser)) {
            return result;
        }

        Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
        CustomerVisibleProductRespDTO customerVisibleProductRespDTO = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);

        if (isPager(result)) {
            // Pager<T>
            handlePager(result, autoSetPrice, customerVisibleProductRespDTO.getItems());
        } else if (result instanceof List<?> list) {
            // List<T>
            handleList(list, autoSetPrice, customerVisibleProductRespDTO.getItems());
        } else if (isVO(result, autoSetPrice.productIdField())) {
            // 单对象 T
            handleObject(result, autoSetPrice, customerVisibleProductRespDTO.getItems());
        } else {
            // 其他类型不处理
            return result;
        }

        return result;
    }

    /** 判断是否是 PageResult<T> */
    private boolean isPager(Object obj) {
        return obj.getClass().getSimpleName().equals("PageResult");
    }

    /** 判断是否是 VO (简单通过有 productIdField 和 priceField) */
    private boolean isVO(Object obj, String productFile) {
        try {
            obj.getClass().getDeclaredField(productFile);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
    }

    private void handlePager(Object pagerObj, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
        try {
            if (pagerObj instanceof PageResult) {
                PageResult pageResult = (PageResult) pagerObj;
                handleList(pageResult.getList(), autoSetPrice, priceDataList);
            }
        } catch (Exception e) {
            log.error("error:", e);
            throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
        }
    }

    private void handleList(List<?> list, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
        List<Long> ids = new ArrayList<>();
        for (Object obj : list) {
            Long id = getProductId(obj, autoSetPrice);
            if (id != null) ids.add(id);
        }
        if (ids.isEmpty()) return;

        for (Object obj : list) {
            Long id = getProductId(obj, autoSetPrice);
            if (id != null) {
                Integer supplierQuote = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                        .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                        .getSupplierQuote();
                Integer marketPrice = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                        .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                        .getMarketPrice();
                BigDecimal discount = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                        .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                        .getFloatingRate();
                setPrice(obj, autoSetPrice, supplierQuote);
                setMarketPrice(obj, autoSetPrice, marketPrice);
                setDiscount(obj, autoSetPrice, discount);
            }
        }
    }

    private void handleObject(Object obj, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
        Long id = getProductId(obj, autoSetPrice);
        if (id == null) return;
        Integer supplierQuote = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                .getSupplierQuote();
        Integer marketPrice = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                .getMarketPrice();
        BigDecimal discount = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
                .orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
                .getFloatingRate();
        setPrice(obj, autoSetPrice, supplierQuote);
        setMarketPrice(obj, autoSetPrice, marketPrice);
        setDiscount(obj, autoSetPrice, discount);
    }

    /** 获取 productId，并做类型校验 */
    private Long getProductId(Object obj, AutoSetPrice autoSetPrice) {
        try {
            Field field = obj.getClass().getDeclaredField(autoSetPrice.productIdField());
            field.setAccessible(true);
            if (!field.getType().equals(Long.class)) {
                log.error(String.format("字段 %s 必须是 Long 类型，实际是 %s",
                        autoSetPrice.productIdField(), field.getType().getSimpleName()));
                throw exception(
                        String.format("字段 %s 必须是 Long 类型，实际是 %s",
                                autoSetPrice.productIdField(), field.getType().getSimpleName())
                );
            }
            return (Long) field.get(obj);
        } catch (NoSuchFieldException e) {
            log.error("未找到字段: " + autoSetPrice.productIdField(), e);
            throw exception("未找到字段: " + autoSetPrice.productIdField());
        } catch (Exception e) {
            throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
        }
    }

    /** 设置 price，并做类型校验 */
    private void setPrice(Object obj, AutoSetPrice autoSetPrice, Integer value) {
        try {
            Field field = obj.getClass().getDeclaredField(autoSetPrice.priceField());
            field.setAccessible(true);
            if (!field.getType().equals(Integer.class) && !field.getType().equals(int.class)) {
                log.error(String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                                autoSetPrice.priceField(), field.getType().getSimpleName()));
                throw exception(
                        String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                                autoSetPrice.priceField(), field.getType().getSimpleName())
                );
            }
            field.set(obj, value);
        } catch (NoSuchFieldException e) {
            log.error("未找到字段: " + autoSetPrice.priceField(), e);
            // throw exception("未找到字段: " + autoSetPrice.priceField());
        } catch (Exception e) {
            // throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
        }
    }

    private void setDiscount(Object obj, AutoSetPrice autoSetPrice, BigDecimal discount) {
        try {
            Field field = obj.getClass().getDeclaredField(autoSetPrice.discountFiled());
            field.setAccessible(true);
            if (!field.getType().equals(BigDecimal.class)) {
                log.error(String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                        autoSetPrice.discountFiled(), field.getType().getSimpleName()));
                throw exception(
                        String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                                autoSetPrice.discountFiled(), field.getType().getSimpleName())
                );
            }
            field.set(obj, discount);
        } catch (NoSuchFieldException e) {
            log.error("未找到字段: " + autoSetPrice.priceField(), e);
            // throw exception("未找到字段: " + autoSetPrice.priceField());
        } catch (Exception e) {
            // throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
        }
    }

    private void setMarketPrice(Object obj, AutoSetPrice autoSetPrice, Integer marketPrice) {
        try {
            Field field = obj.getClass().getDeclaredField(autoSetPrice.marketPriceFiled());
            field.setAccessible(true);
            if (!field.getType().equals(Integer.class) && !field.getType().equals(int.class)) {
                log.error(String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                        autoSetPrice.marketPriceFiled(), field.getType().getSimpleName()));
                throw exception(
                        String.format("字段 %s 必须是 int/Integer 类型，实际是 %s",
                                autoSetPrice.marketPriceFiled(), field.getType().getSimpleName())
                );
            }
            field.set(obj, marketPrice);
        } catch (NoSuchFieldException e) {
            log.error("未找到字段: " + autoSetPrice.priceField(), e);
            // throw exception("未找到字段: " + autoSetPrice.priceField());
        } catch (Exception e) {
            // throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
        }
    }
}
