package com.cf.imes.module.system.service.funds.delay;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.date.LocalDateTimeUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.PreviouProductDelayRespVO;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayRespVO;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelaySaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.delay.ProductDelayRecordDO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
import com.cf.imes.module.system.dal.mysql.funds.delay.ProductDelayRecordMapper;
import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper;
import com.cf.imes.module.system.enums.pay.DurationUnitEnum;
import com.cf.imes.module.system.enums.pay.PurchaseRecordStatusEnum;
import com.cf.imes.module.system.service.funds.products.ProductsDetailService;
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
import com.cf.imes.module.system.service.organ.OrganService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;
import java.util.Objects;

import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_DELAY_UNIT_NOT_SUPPORT;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PURCHASE_NO_EXIST;

/**
 * 产品延期 ServiceImpl 实现类
 *
 * @author Gqr
 * @since 2025/9/15 11:22
 */
@Service
public class ProductDelayServiceImpl implements ProductDelayService {
    @Resource
    private OrganService organService;

    @Resource
    private PurchaseService purchaseService;

    @Resource
    private ProductsDetailService productsDetailService;

    @Resource
    private ProductDelayRecordMapper productDelayRecordMapper;

    @Resource
    private PurchaseRecordMapper purchaseRecordMapper;

    @Override
    public Long createProductDelay(ProductDelaySaveReqVO productDelaySaveReqVO) {
        Long organId = productDelaySaveReqVO.getOrganId();
        // 校验组织
        OrganizationDO organizationDO = organService.validOrgan(organId);

        // 获取产品购买记录
        Long purchaseId = productDelaySaveReqVO.getPurchaseId();
        PurchaseRecordDO purchaseRecordDO = purchaseService.getRecord(purchaseId);
        AssertUtils.notEmpty(purchaseRecordDO, PRODUCT_PURCHASE_NO_EXIST);

        // 校验产品和定价规则
        productsDetailService.validateProductExist(purchaseRecordDO.getProductId());

        /**
         * 查询是否有前序的对应产品延期记录：
         * 1、如果有记录，延期有效期在其记录上叠加
         * 2、如果没有记录，取购买记录的结束时间作为延期的开始时间，如果结束时间已经过期了，那么从今天开始算
         */
        ProductDelayRecordDO lastDelayRecordDO = productDelayRecordMapper.selectOne(new LambdaQueryWrapper<ProductDelayRecordDO>()
                .eq(ProductDelayRecordDO::getOrganId, organId)
                .eq(ProductDelayRecordDO::getPurchaseId, purchaseId)
                .eq(ProductDelayRecordDO::getProductId, purchaseRecordDO.getProductId())
                .orderByDesc(ProductDelayRecordDO::getCreateTime)
                .last("limit 1"));

        Integer delayDuration = productDelaySaveReqVO.getDelayDuration();
        Integer delayDurationUnit = productDelaySaveReqVO.getDelayDurationUnit();
        LocalDate endTime;
        LocalDate now = LocalDate.now();
        if(ObjectUtil.isNotNull(lastDelayRecordDO)) {
            endTime = lastDelayRecordDO.getDelayTime();
        } else {
            endTime = purchaseRecordDO.getEndTime();
            if (endTime.isBefore(now)) {
                endTime = now;
            }
        }

        // 计算延期后有效时间
        LocalDate delayTime = calculateEndTime(endTime, delayDuration, delayDurationUnit);
        // 创建延期记录
        ProductDelayRecordDO productDelayRecordDO = ProductDelayRecordDO.builder()
                .organId(organId)
                .organName(organizationDO.getName())
                .productId(purchaseRecordDO.getProductId())
                .productName(purchaseRecordDO.getProductName())
                .delayDuration(delayDuration)
                .delayDurationUnit(delayDurationUnit)
                .endTime(endTime)
                .delayTime(delayTime)
                .purchaseId(purchaseId)
                .paid(false)
                .build();
        productDelayRecordMapper.insert(productDelayRecordDO);
        return productDelayRecordDO.getId();
    }

    /**
     * 计算延期时间
     *
     * @param startTime
     * @param duration
     * @param durationUnit
     * @return
     */
    private static LocalDate calculateEndTime(LocalDate startTime, Integer duration, Integer durationUnit) {
        return addTime(startTime, duration, durationUnit);
    }

    /**
     * 基于时长和单位累加time
     *
     * @param time
     * @param duration
     * @param unit
     * @return
     */
    private static LocalDate addTime(LocalDate time, int duration, int unit) {
        DurationUnitEnum e = DurationUnitEnum.fromStatus(unit);
        if (Objects.equals(e, DurationUnitEnum.YEAR)) {
            return time.plusYears(duration);
        } else if (Objects.equals(e, DurationUnitEnum.MONTH)) {
            return time.plusMonths(duration);
        } else if (Objects.equals(e, DurationUnitEnum.DAY)) {
            return time.plusDays(duration);
        } else {
            throw new ServiceException(PRODUCT_DELAY_UNIT_NOT_SUPPORT);
        }
    }

    @Override
    public PageResult<ProductDelayRespVO> getPage(ProductDelayPageReqVO pageReqVO) {
        pageReqVO.setCreateTime(LocalDateTimeUtils.generateTimeSection(pageReqVO.getCreateTime()));
        PageResult<ProductDelayRecordDO> productDelayRecordDOPageResult = productDelayRecordMapper.selectPage(pageReqVO);
        return new PageResult<>(BeanUtils.toBean(productDelayRecordDOPageResult.getList(), ProductDelayRespVO.class), productDelayRecordDOPageResult.getTotal());
    }

    @Override
    public List<ProductDelayRespVO> getRecordsByPurchaseId(Long purchaseId, Long organId) {
        return BeanUtil.copyToList(productDelayRecordMapper.selectList(new LambdaQueryWrapper<ProductDelayRecordDO>()
                .eq(ProductDelayRecordDO::getPurchaseId, purchaseId)
                .eq(ProductDelayRecordDO::getOrganId, organId)
                .orderByDesc(ProductDelayRecordDO::getId)
                .select(ProductDelayRecordDO::getDelayDuration, ProductDelayRecordDO::getDelayDurationUnit, ProductDelayRecordDO::getDelayTime, ProductDelayRecordDO::getPaid)), ProductDelayRespVO.class);
    }

    @Override
    public PreviouProductDelayRespVO getActiveRecordsByProductId(Long productId, Long organId) {
        PreviouProductDelayRespVO previouProductDelayRespVO = new PreviouProductDelayRespVO();
        PurchaseRecordDO purchaseRecordDO = purchaseRecordMapper.selectOne(new LambdaQueryWrapperX<PurchaseRecordDO>()
                .eq(PurchaseRecordDO::getOrganId, organId)
                .eq(PurchaseRecordDO::getProductId, productId)
                .eq(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.ACTIVE.getStatus())
                .eq(PurchaseRecordDO::getDeleted, false)
        );
        if (ObjectUtil.isNotNull(purchaseRecordDO)) {
            previouProductDelayRespVO.setPreviouPurchaseRecord(purchaseRecordDO);
            List<ProductDelayRecordDO> recordDOS = productDelayRecordMapper.selectList(new LambdaQueryWrapper<ProductDelayRecordDO>()
                    .eq(ProductDelayRecordDO::getProductId, productId)
                    .eq(ProductDelayRecordDO::getOrganId, organId)
                    .eq(ProductDelayRecordDO::getPurchaseId, purchaseRecordDO.getId())
                    .eq(ProductDelayRecordDO::getDeleted, false)
                    .select(ProductDelayRecordDO::getDelayDuration, ProductDelayRecordDO::getDelayDurationUnit, ProductDelayRecordDO::getDelayTime, ProductDelayRecordDO::getEndTime, ProductDelayRecordDO::getId)
            );
            previouProductDelayRespVO.setDelayRespVOList(BeanUtil.copyToList(recordDOS, ProductDelayRespVO.class));
        }
        return previouProductDelayRespVO;
    }

    @Override
    public void updatePurchaseDelayRecordsPaidAndRemark(List<ProductDelayRecordDO> list, Long organId, Boolean paid) {
        for (ProductDelayRecordDO recordDO : list) {
            productDelayRecordMapper.update(new LambdaUpdateWrapperX<ProductDelayRecordDO>()
                    .eq(ProductDelayRecordDO::getId, recordDO.getId())
                    .eq(ProductDelayRecordDO::getOrganId, organId)
                    .setIfPresent(ProductDelayRecordDO::getPaid, paid)
                    .setIfPresent(ProductDelayRecordDO::getRemark, recordDO.getRemark()));
        }
    }

    @Override
    public void updatePurchaseDelayRecordsPaid(Long purchaseId, Long organId) {
        productDelayRecordMapper.update(new LambdaUpdateWrapper<ProductDelayRecordDO>()
                .eq(ProductDelayRecordDO::getPurchaseId, purchaseId)
                .eq(ProductDelayRecordDO::getOrganId, organId)
                .set(ProductDelayRecordDO::getPaid, true));
    }

    @Override
    public void resetPurchaseDelayRecordsRemark(Long purchaseId, Long organId) {
        productDelayRecordMapper.update(new LambdaUpdateWrapper<ProductDelayRecordDO>()
                .eq(ProductDelayRecordDO::getPurchaseId, purchaseId)
                .eq(ProductDelayRecordDO::getOrganId, organId)
                .set(ProductDelayRecordDO::getRemark, null));
    }

    @Override
    public void deleteProductDelayRecords(List<Long> ids, Long organId) {
        productDelayRecordMapper.delete(new LambdaUpdateWrapper<ProductDelayRecordDO>()
                .in(ProductDelayRecordDO::getId, ids)
                .eq(ProductDelayRecordDO::getOrganId, organId));
    }
}
