package cn.iocoder.yudao.module.member.service.billing;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.member.controller.admin.billing.vo.BillingPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.billing.vo.BillingRespMoneyVO;
import cn.iocoder.yudao.module.member.controller.admin.billing.vo.BillingSaveReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.billing.BillingDO;
import cn.iocoder.yudao.module.member.dal.dataobject.order.DishOrderDO;
import cn.iocoder.yudao.module.member.dal.dataobject.orderspacecapsule.OrderSpaceCapsuleDO;
import cn.iocoder.yudao.module.member.dal.dataobject.storeorder.StoreOrderDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.billing.BillingMapper;
import cn.iocoder.yudao.module.member.dal.mysql.order.DishOrderMapper;
import cn.iocoder.yudao.module.member.dal.mysql.orderspacecapsule.OrderSpaceCapsuleMapper;
import cn.iocoder.yudao.module.member.dal.mysql.storeorder.StoreOrderMapper;
import cn.iocoder.yudao.module.member.enums.BillingEnum;
import cn.iocoder.yudao.module.member.enums.BillingStatusEnum;
import cn.iocoder.yudao.module.member.enums.SpaceCapsuleOrderEnum;
import cn.iocoder.yudao.module.member.enums.StoreOrderStatusEnum;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;


import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 开票记录 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
public class BillingServiceImpl implements BillingService {

    @Resource
    private BillingMapper billingMapper;
    @Resource
    private MemberUserService userService;
    @Resource
    private OrderSpaceCapsuleMapper orderSpaceCapsuleMapper;
    @Resource
    private DishOrderMapper dishOrderMapper;
    @Resource
    private StoreOrderMapper storeOrderMapper;

    /**
     * 食堂
     * @param startTime
     * @param endTime
     * @return
     */
    public List<DishOrderDO> getDishOrderDO(Date startTime, Date endTime, Set<Long> ids){
        return dishOrderMapper.selectList(new LambdaQueryWrapperX<DishOrderDO>()
                .betweenIfPresent(DishOrderDO::getCreateTime,startTime,endTime)
                .eq(DishOrderDO::getOrderStatus,DishOrderDO.COMPLETE)
                .ne(DishOrderDO::getBillingExist,Boolean.TRUE)
                .in(ObjUtil.isNotEmpty(ids), DishOrderDO::getId,ids)
        );
    }
    /**
     * 超市
     * @param startTime
     * @param endTime
     * @return
     */
    public List<StoreOrderDO> getStoreOrderDO(Date startTime, Date endTime, Set<Long> ids){
        return storeOrderMapper.selectList(new LambdaQueryWrapperX<StoreOrderDO>()
                .betweenIfPresent(StoreOrderDO::getCreateTime,startTime,endTime)
                .eq(StoreOrderDO::getStatus, StoreOrderStatusEnum.COMPLETE.getCode())
                .ne(StoreOrderDO::getBillingExist,Boolean.TRUE)
                .in(ObjUtil.isNotEmpty(ids), StoreOrderDO::getOrderId,ids)
        );
    }
    /**
     * 太空舱
     * @param startTime
     * @param endTime
     * @return
     */
    public List<OrderSpaceCapsuleDO> getOrderSpaceCapsuleDO(Date startTime, Date endTime, Set<Long> ids){
        return  orderSpaceCapsuleMapper.selectList(new LambdaQueryWrapperX<OrderSpaceCapsuleDO>()
                .betweenIfPresent(OrderSpaceCapsuleDO::getCreateTime,startTime,endTime)
                .eq(OrderSpaceCapsuleDO::getStatus, SpaceCapsuleOrderEnum.SPACE_CAPSULE_ORDER_ENUM_DONE.getCode())
                .ne(OrderSpaceCapsuleDO::getBillingExist,Boolean.TRUE)
                .in(ObjUtil.isNotEmpty(ids), OrderSpaceCapsuleDO::getId,ids)
        );
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createBilling(BillingSaveReqVO createReqVO) {
        // 插入 BillingDO 对象并生成唯一主键
        BillingDO billing = BeanUtils.toBean(createReqVO, BillingDO.class);
        billing.setId(IdUtil.getSnowflakeNextId() + "");
        // 获取用户信息
        MemberUserDO user = userService.getUser(createReqVO.getUserId());
        billing.setUserName(user.getNickname());
        billing.setUserPhone(user.getMobile());
        // 根据不同的 billingType 获取订单、金额和订单 ID 列表，并设置开票信息
        List<Long> orderIds;
        BigDecimal totalMoney;
        String billingName;
        switch (BillingEnum.fromCode(billing.getBillingType())) {
            case BILLING_CANTEEN:
                List<DishOrderDO> dishOrders = getDishOrderDO(createReqVO.getStartTime(), createReqVO.getEndTime(), createReqVO.getOrderId());
                if (CollUtil.isEmpty(dishOrders)) throw exception(BILLING_NOT_ORDER_EXISTS);
                totalMoney = calculateTotalMoney(dishOrders, DishOrderDO::getTotalMoney);
                orderIds = getOrderIds(dishOrders, DishOrderDO::getId);
                billingName = "食堂餐饮费";
                updateBillingExist(dishOrders, dishOrderMapper);
                break;
            case BILLING_SUPERMARKET:
                List<StoreOrderDO> storeOrders = getStoreOrderDO(createReqVO.getStartTime(), createReqVO.getEndTime(), createReqVO.getOrderId());
                if (CollUtil.isEmpty(storeOrders)) throw exception(BILLING_NOT_ORDER_EXISTS);
                totalMoney = calculateTotalMoney1(storeOrders, StoreOrderDO::getTotalPrice);
                List<Integer> orderIds1 = getOrderIds(storeOrders, StoreOrderDO::getOrderId);
                orderIds = orderIds1.stream().map(Integer::longValue).collect(Collectors.toList());
                billingName = "超市物品费";
                updateBillingExist(storeOrders, storeOrderMapper);
                break;
            case BILLING_SPACE_CAPSULE:
                List<OrderSpaceCapsuleDO> capsuleOrders = getOrderSpaceCapsuleDO(createReqVO.getStartTime(), createReqVO.getEndTime(), createReqVO.getOrderId());
                if (CollUtil.isEmpty(capsuleOrders)) throw exception(BILLING_NOT_ORDER_EXISTS);
                totalMoney = calculateTotalMoney(capsuleOrders, OrderSpaceCapsuleDO::getMoney);
                orderIds = getOrderIds(capsuleOrders, OrderSpaceCapsuleDO::getId);
                billingName = "太空舱费用";
                updateBillingExist(capsuleOrders, orderSpaceCapsuleMapper);
                break;
            default:
                throw new IllegalArgumentException("Unsupported billing type");
        }
        // 设置 BillingDO 对象的基本信息
        billing.setBillingMoney(totalMoney);
        billing.setStatus(BillingStatusEnum.BILLING_INVOICING.getCode());
        billing.setBillingName(billingName);
        billing.setOrderId(JsonUtils.toJsonString(orderIds));
        billing.setRequestBillingTime(LocalDateTime.now());
        // 插入 BillingDO 对象到数据库
        billingMapper.insert(billing);
        // 返回主键 ID
        return billing.getId();
    }
    // 计算订单的总金额，处理 Double 到 BigDecimal 的转换
    private <T> BigDecimal calculateTotalMoney1(List<T> orders, Function<T, Double> moneyMapper) {
        return orders.stream()
                .map(moneyMapper)
                .filter(Objects::nonNull) // 过滤掉 null 值，避免空指针异常
                .map(BigDecimal::valueOf) // 将 Double 转换为 BigDecimal
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 求和
    }
    // 计算订单的总金额
    private <T> BigDecimal calculateTotalMoney(List<T> orders, Function<T, BigDecimal> moneyMapper) {
        return orders.stream()
                .map(moneyMapper)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    // 获取订单 ID 列表
    private <T, R> List<R> getOrderIds(List<T> orders, Function<T, R> idMapper) {
        return orders.stream()
                .map(idMapper)
                .collect(Collectors.toList());
    }
    // 更新订单为开票状态
    private <T> void updateBillingExist(List<T> orders, BaseMapperX<T> mapper) {
        orders.forEach(order -> {
            // Assuming that the order has a `setBillingExist` method
            try {
                Method setBillingExist = order.getClass().getMethod("setBillingExist", Boolean.class);
                setBillingExist.invoke(order, Boolean.TRUE);
            } catch (Exception e) {
                throw new RuntimeException("Error setting billing exist status", e);
            }
        });
        mapper.updateBatch(orders);
    }
    @Override
    public void updateBilling(BillingSaveReqVO updateReqVO) {
        // 校验存在
        BillingDO billingDO = billingMapper.selectById(updateReqVO.getId());
        if (billingDO== null) {
            throw exception(BILLING_NOT_EXISTS);
        }
        // 更新
        BillingDO updateObj = BeanUtils.toBean(updateReqVO, BillingDO.class);
        updateObj.setOrderId(billingDO.getOrderId());
        //开票成功是根据pdf上传成功没
        if(StrUtil.isNotBlank(updateObj.getPdfUrl())){
            //上传成功 修改状态
            updateObj.setStatus(BillingStatusEnum.BILLING_INVOICING_IS_COMPLETE.getCode());
            //设置同意人
            updateObj.setSystemId(SecurityFrameworkUtils.getLoginUserId());
        }else{
            //开票回拒
            if(StrUtil.isBlank(updateObj.getRefuseDetails())){
                throw exception(BILLING_NOT_ORDER_REFUSE);
            }
            updateObj.setStatus(BillingStatusEnum.BILLING_INVOICING_REJECTION.getCode());
            updateObj.setRefuseTime(LocalDateTime.now());
            String orderId = updateObj.getOrderId();
            JSONArray jsonArray = JSONUtil.parseArray(orderId);
            // 转换为 Set<Long>
            Set<Long> orderIds = jsonArray.stream()
                    .map(object -> Long.parseLong(object.toString())) // 将每个元素转换为 Long
                    .collect(Collectors.toSet());
            //拒绝就把锁住的订单释放掉
            switch (BillingEnum.fromCode(updateObj.getBillingType())){
                case BILLING_CANTEEN:
                    dishOrderMapper.updateByIdBillingExist(orderIds,Boolean.FALSE);
                    break;
                case BILLING_SUPERMARKET:
                    storeOrderMapper.updateByIdBillingExist(orderIds,Boolean.FALSE);
                    break;
                case BILLING_SPACE_CAPSULE:
                    orderSpaceCapsuleMapper.updateByIdBillingExist(orderIds,Boolean.FALSE);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported billing type");
            }
        }
        billingMapper.updateById(updateObj);
    }

    @Override
    public void deleteBilling(String id) {
        // 校验存在
        validateBillingExists(id);
        // 删除
        billingMapper.deleteById(id);
    }

    private void validateBillingExists(String id) {
        if (billingMapper.selectById(id) == null) {
            throw exception(BILLING_NOT_EXISTS);
        }
    }
    @Override
    public BillingDO getBilling(String id) {
        return billingMapper.selectById(id);
    }

    @Override
    public PageResult<BillingDO> getBillingPage(BillingPageReqVO pageReqVO) {
        return billingMapper.selectPage(pageReqVO);
    }

    @Override
    public BillingRespMoneyVO getHaveWithoutMoney(Long userId) {
        BillingRespMoneyVO b=new BillingRespMoneyVO();
        //查询开票的订单
        BigDecimal reduce = billingMapper.selectList(new LambdaQueryWrapperX<BillingDO>()
                        .eq(BillingDO::getStatus, BillingStatusEnum.BILLING_INVOICING_IS_COMPLETE.getCode())
                        .eq(BillingDO::getUserId,userId)
                ).stream()
                .map(BillingDO::getBillingMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add);// 求和
        b.setHaveMoney(reduce);
        BigDecimal reduce1 = orderSpaceCapsuleMapper.selectList(new LambdaQueryWrapperX<OrderSpaceCapsuleDO>()
                        .eq(OrderSpaceCapsuleDO::getStatus, SpaceCapsuleOrderEnum.SPACE_CAPSULE_ORDER_ENUM_DONE.getCode())
                        .eq(OrderSpaceCapsuleDO::getBillingExist, Boolean.FALSE)
                        .eq(OrderSpaceCapsuleDO::getUserId,userId)
                )
                .stream().map(OrderSpaceCapsuleDO::getMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add);// 求和
        BigDecimal reduce2 = dishOrderMapper.selectList(new LambdaQueryWrapperX<DishOrderDO>()
                        .eq(DishOrderDO::getOrderStatus, DishOrderDO.COMPLETE)
                        .eq(DishOrderDO::getBillingExist, Boolean.FALSE).eq(DishOrderDO::getUserId,userId))
                .stream().map(DishOrderDO::getTotalMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add);// 求和
        BigDecimal reduce3 = storeOrderMapper.selectList(new LambdaQueryWrapperX<StoreOrderDO>()
                        .eq(StoreOrderDO::getStatus, StoreOrderStatusEnum.COMPLETE.getCode())
                        .eq(StoreOrderDO::getBillingExist, Boolean.FALSE).eq(StoreOrderDO::getUserId,userId))
                .stream().map(f-> BigDecimal.valueOf(f.getTotalPrice()))
                .reduce(BigDecimal.ZERO, BigDecimal::add);// 求和
        BigDecimal add = reduce1.add(reduce2).add(reduce3);
        b.setWithoutMoney(add);
        return b;
    }

}