package cn.iocoder.yudao.module.system.service.rental;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.order.RentalOrderPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.order.RentalOrderRespVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.order.RentalOrderSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.rental.RentalOrderDO;
import cn.iocoder.yudao.module.system.dal.mysql.rental.RentalOrderMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.RENTAL_ORDER_CUSTOMER_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.RENTAL_ORDER_NOT_EXISTS;

/**
 * 租赁订单 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
@Slf4j
public class RentalOrderServiceImpl implements RentalOrderService {

    @Resource
    private RentalOrderMapper rentalOrderMapper;

    @Resource
    @Lazy
    private RentalItemsRecordService rentalItemsRecordService;

    @Resource
    @Lazy
    private RentalDepositRecordService rentalDepositRecordService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Long createRentalOrder(RentalOrderSaveReqVO createReqVO) {
        // 检验当前客户是否已存在订单
        Long count = rentalOrderMapper.selectCount(new LambdaQueryWrapperX<RentalOrderDO>()
                .eq(RentalOrderDO::getCustomerId, createReqVO.getCustomerId())
                .ne(RentalOrderDO::getStatus, 4));
        if (count > 0L) {
            throw exception(RENTAL_ORDER_CUSTOMER_EXISTS);
        }

        // 新增订单DO
        RentalOrderDO rentalOrder = BeanUtils.toBean(createReqVO, RentalOrderDO.class);

        // 获取当前日期
        String now = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "rental_order_no_" + now;
        // 获取分布式锁
        String LOCK_KEY = "lock:rental:order:create";
        RLock lock = redissonClient.getLock(LOCK_KEY);
        try {
            lock.lock();
            // redis 获取当天订单号
            String no = stringRedisTemplate.opsForValue().get(key);
            if (no != null) {
                no = "ZL" + now + String.format("%03d", Integer.parseInt(no) + 1);
                // redis 缓存订单号
                stringRedisTemplate.opsForValue().increment(key, 1);
            }else {
                no = "ZL" + now + String.format("%03d", 1);
                // redis 缓存订单号
                stringRedisTemplate.opsForValue().set(key, "1", 1, TimeUnit.DAYS);
            }
            // 设置订单编号
            rentalOrder.setOrderNo(no);
            createReqVO.getDepositRecords().forEach(item -> item.setOrderNo(rentalOrder.getOrderNo()));
            createReqVO.getItemsRecords().forEach(item -> item.setOrderNo(rentalOrder.getOrderNo()));
        } catch (Exception ex) {
            log.error("[messageResend][执行异常]", ex);
        } finally {
            lock.unlock();
        }

        // 插入订单
        rentalOrderMapper.insert(rentalOrder);

        // 同步插入租赁物品记录
        rentalItemsRecordService.createRentalItemsRecordBath(createReqVO.getItemsRecords());
        if (CollectionUtil.isNotEmpty(createReqVO.getDepositRecords())) {
            // 同步插入租赁押金记录
            rentalDepositRecordService.createRentalDepositRecordBatch(createReqVO.getDepositRecords());
        }

        // 返回
        return rentalOrder.getId();
    }

    @Override
    @CachePut(value = RedisKeyConstants.RENTAL_ORDER_AMOUNT, key = "#updateReqVO.orderNo", condition = "#updateReqVO.status == 1")
    public BigDecimal updateRentalOrder(RentalOrderSaveReqVO updateReqVO) {
        // 校验存在
        validateRentalOrderExists(updateReqVO.getId());
        // 更新
        RentalOrderDO updateObj = BeanUtils.toBean(updateReqVO, RentalOrderDO.class);
        rentalOrderMapper.updateById(updateObj);

        return updateObj.getDepositAmount().subtract(updateObj.getChargebacksAmount() == null ? BigDecimal.ZERO : updateObj.getChargebacksAmount());
    }

    @Override
    public void update(RentalOrderDO updateReqVO) {
        Long count = rentalOrderMapper.selectCount(RentalOrderDO::getOrderNo, updateReqVO.getOrderNo());
        if (count <= 0L) {
            throw exception(RENTAL_ORDER_NOT_EXISTS);
        }

        // 更新
        rentalOrderMapper.update(updateReqVO, new LambdaQueryWrapperX<RentalOrderDO>()
                .eq(RentalOrderDO::getOrderNo, updateReqVO.getOrderNo()));
    }

    @Override
    public void deleteRentalOrder(Long id) {
        // 校验存在
        validateRentalOrderExists(id);
        // 删除
        rentalOrderMapper.deleteById(id);
    }

    private void validateRentalOrderExists(Long id) {
        if (rentalOrderMapper.selectById(id) == null) {
            throw exception(RENTAL_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public RentalOrderDO getRentalOrder(Long id) {

        return rentalOrderMapper.selectById(id);
    }

    @Override
    public RentalOrderDO getRentalOrder(String orderNo) {

        return rentalOrderMapper.selectOne(RentalOrderDO::getOrderNo, orderNo);
    }

    @Override
    public PageResult<RentalOrderRespVO> getRentalOrderPage(RentalOrderPageReqVO pageReqVO) {
        return rentalOrderMapper.selectPage(pageReqVO);
    }

    @Override
    @Cacheable(value = RedisKeyConstants.RENTAL_ORDER_AMOUNT, key = "#orderNo", unless = "#result == null")
    public BigDecimal getOrderAmount(String orderNo) {

        return rentalOrderMapper.selectOrderAmount(orderNo);
    }

    @Override
    public Long getCustomerIdByOrderNo(String orderNo) {
        RentalOrderDO orderDO = rentalOrderMapper.selectOne(RentalOrderDO::getOrderNo, orderNo);
        if (orderDO != null) {
            return orderDO.getCustomerId();
        }
        return null;
    }
}