package com.clx.performance.event;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.json.JSONUtil;
import com.clx.performance.component.OrderChildLoanComponent;
import com.clx.performance.constant.RabbitKeyConstants;
import com.clx.performance.constant.RedissonConstants;
import com.clx.performance.dao.loan.OrderChildLoanRetryRecordDao;
import com.clx.performance.dao.loan.OwnerLoanAccountRunningWaterRecordDao;
import com.clx.performance.dao.loan.OwnerRepaymentDao;
import com.clx.performance.dao.settle.SettlementDriverDao;
import com.clx.performance.dao.settle.SettlementDriverDetailDao;
import com.clx.performance.dao.settle.SettlementOwnerDetailDao;
import com.clx.performance.dto.OwnerLoanMqDTO;
import com.clx.performance.enums.PerformanceResultEnum;
import com.clx.performance.enums.loan.OwnerLoanRecordEnum;
import com.clx.performance.enums.loan.OwnerRePaymentEnum;
import com.clx.performance.enums.settle.SettlementOwnerEnum;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.loan.OrderChildLoanRetryRecord;
import com.clx.performance.model.loan.OwnerLoanAccountRunningWaterRecord;
import com.clx.performance.model.loan.OwnerRepayment;
import com.clx.performance.model.settle.SettlementDriverDetail;
import com.clx.performance.model.settle.SettlementOwnerDetail;
import com.clx.performance.service.settle.SettlementMqService;
import com.msl.common.base.Optional;
import com.msl.common.exception.ServiceSystemException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
@AllArgsConstructor
public class EventListenerComponent {

    private final SettlementOwnerDetailDao settlementOwnerDetailDao;

    private final SettlementDriverDetailDao settlementDriverDetailDao;

    private final OrderChildLoanRetryRecordDao orderChildLoanRetryRecordDao;

    private final OwnerRepaymentDao ownerRepaymentDao;

    private final OrderChildLoanComponent orderChildLoanComponent;

    private final SettlementMqService settlementMqService;

    private final ApplicationEventPublisher applicationEventPublisher;

    private final SettlementDriverDao settlementDriverDao;

    private final OwnerLoanAccountRunningWaterRecordDao ownerLoanAccountRunningWaterRecordDao;

    private final RabbitTemplate rabbitTemplate;

    private final RedissonClient redissonClient;


    @Async
    @TransactionalEventListener(classes = {SettlementUpdateEvent.class}, phase = TransactionPhase.AFTER_ROLLBACK, fallbackExecution = true)
    public void listen(SettlementUpdateEvent event) {
        log.info("SettlementUpdateEvent事件执行");
        SettlementOwnerDetail settlementOwnerDetail = event.getSettlementOwnerDetail();
        SettlementDriverDetail settlementDriverDetail = event.getSettlementDriverDetail();
        settlementOwnerDetailDao.updateInvoiceType(settlementOwnerDetail);
        settlementDriverDetailDao.updateInvoiceTypeAndPrepayFreightFlag(settlementDriverDetail);
        Integer ownerId = settlementOwnerDetail.getId();
        Integer driverId = settlementDriverDetail.getId();

        OrderChildLoanRetryRecord record = orderChildLoanRetryRecordDao.selectOneByOwnerIdAndDriverId(ownerId, driverId);
        if (record == null) {
            OrderChildLoanRetryRecord entity = new OrderChildLoanRetryRecord();
            entity.setSettlementOwnerId(ownerId);
            entity.setSettlementDriverId(driverId);
            entity.setOwnerUserNo(settlementOwnerDetail.getOwnerUserNo());
            log.info("当前货主结算信息{},车主结算信息：{}", JSONUtil.parse(settlementOwnerDetail), JSONUtil.parse(settlementDriverDetail));
            // 保存重试记录
            orderChildLoanRetryRecordDao.saveEntity(entity);
        }

    }

    @EventListener(classes = {OwnerRepaymentUpdateEvent.class})
    public void listen(OwnerRepaymentUpdateEvent event) {
        log.info("OwnerRepaymentUpdateEvent事件执行");
        OwnerRepayment update = new OwnerRepayment();
        update.setId(event.getId());
        update.setBeOverdue(OwnerRePaymentEnum.BeOverdue.YES.getCode());
        ownerRepaymentDao.updateEntityByKey(update);
    }

    @Async
    @TransactionalEventListener(classes = {OwnerLoanEvent.class}, phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
    public void listen(OwnerLoanEvent event) {
        log.info("OwnerLoanEvent事件执行");
        OwnerLoanMqDTO param = event.getParam();

        RLock lock = null;
        try {
            lock = redissonClient.getLock(RedissonConstants.OWNER_LOAN_EVENT_USERID_LOCK + param.getSettlementOwnerDetail().getOwnerUserNo());
            boolean flag = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (!flag) {
                throw new ServiceSystemException(PerformanceResultEnum.GET_LOAN_RECORD);
            }

            SettlementOwnerDetail settlementOwnerDetail = param.getSettlementOwnerDetail();
            SettlementDriverDetail settlementDriverDetail = param.getSettlementDriverDetail();
            OrderChild orderChild = param.getOrderChild();

            if (SettlementOwnerEnum.InvoiceType.ORDINARY.getCode().equals(settlementDriverDetail.getInvoiceType())) {
                applicationEventPublisher.publishEvent(new OwnerLoanThawEvent(this, orderChild.getChildNo()));
            }
            else {
                try {
                    //生成借款标识
                    orderChildLoanComponent.childLoanConfirmAfterProcess(settlementDriverDetail, settlementOwnerDetail, orderChild);
                } catch (Exception e) {
                    //未借款
                    settlementDriverDetail.setLoanFlag(OwnerLoanRecordEnum.LoanFlag.NO_LOAN.getCode());
                    applicationEventPublisher.publishEvent(new SettlementUpdateEvent(this, settlementDriverDetail, settlementOwnerDetail));
                    log.error("处理货主借款监听器执行异常,数据为{},异常信息{}", JSONUtil.parse(param), ExceptionUtil.getMessage(e));
                    throw e;
                }
            }
            log.info("处理货主借款监听器执行成功");

            settlementOwnerDetailDao.updateInvoiceType(settlementOwnerDetail);
            settlementDriverDetailDao.updateInvoiceTypeAndPrepayFreightFlag(settlementDriverDetail);

            // 发送mq 结算统计
            settlementMqService.settlementStatistics(settlementDriverDetail.getChildNo());
        } catch (Exception e) {
            throw new ServiceSystemException(PerformanceResultEnum.GET_LOAN_RECORD, e.getMessage());
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }


    @EventListener(classes = {OwnerLoanFlagEvent.class})
    public void listen(OwnerLoanFlagEvent event) {
        log.info("OwnerLoanFlagEvent事件执行");
        Long loanNo = event.getLoanNo();
        List<OwnerLoanAccountRunningWaterRecord> records = ownerLoanAccountRunningWaterRecordDao.selectLoanRunningWatterRecordByLoanNoForRepay
                (loanNo);

        Map<String, List<OwnerLoanAccountRunningWaterRecord>> listMap = records.stream().collect(Collectors.groupingBy(OwnerLoanAccountRunningWaterRecord::getChildNo));
        List<String> childNoList = new LinkedList<>();
        for (Map.Entry<String, List<OwnerLoanAccountRunningWaterRecord>> entry : listMap.entrySet()) {
            if (entry.getValue().size() == 1) {
                childNoList.add(entry.getKey());
            } else {
                boolean flag = true;
                for (OwnerLoanAccountRunningWaterRecord record : entry.getValue()) {
                    if (record.getLoanNo().equals(loanNo)) {
                        continue;
                    }

                    Optional<OwnerRepayment> optional = ownerRepaymentDao.getOneByField(OwnerRepayment::getLoanNo, record.getLoanNo());
                    if (!optional.isPresent()) {
                        //没有还款记录
                        flag = false;
                        break;
                    } else {
                        OwnerRepayment ownerRepayment = optional.get();
                        //其他流水记录对应的还款单不是付款成功
                        if (!ownerRepayment.getStatus().equals(OwnerRePaymentEnum.Status.PAY_SUCCESS.getCode())) {
                            log.info("当前运单号{},对应多条流水,其中流水号：{},对应的还款单号{},未还款", entry.getKey(), record.getRunningWaterNo(), ownerRepayment.getRepaymentNo());
                            flag = false;
                            break;
                        }
                    }
                }
                if (flag) {
                    childNoList.add(entry.getKey());
                }
            }
        }

        log.info("需要更新计费明细借款标识为已还款的运单号：{}", JSONUtil.parse(childNoList));
        if (CollectionUtil.isNotEmpty(childNoList)) {
            settlementDriverDetailDao.updateLoanFlagByChildNoList(childNoList);
            settlementDriverDao.updateLoanFlagByChildNoList(childNoList);
        }

    }

    @EventListener(classes = {OrderChildCancelEvent.class})
    public void listen(OrderChildCancelEvent event) {
        log.info("OrderChildCancelEvent事件执行");
        String childNo = event.getChildNo();
        log.info("需要解冻借款的的运单号:{}", childNo);

        orderChildLoanComponent.orderChildCancelAfter(childNo);
    }

    @EventListener(classes = {OwnerLoanThawEvent.class})
    public void listen(OwnerLoanThawEvent event) {
        log.info("OwnerLoanThawEvent事件执行");
        log.info("需要解冻借款的的运单号:{}", event.getChildNo());
        Message message = MessageBuilder.withBody(event.getChildNo().getBytes()).build();


        rabbitTemplate.send(
                RabbitKeyConstants.OWNER_LOAN_EXCHANGE, RabbitKeyConstants.OWNER_LOAN_THAW_ROUTE_KEY, message
        );

    }
}
