package cn.superdesk.uniorder.service.invoice.customer.state.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.superdesk.uniorder.common.enums.invoice.customer.InvoiceApplyCustomerBackAccountStatusEnum;
import cn.superdesk.uniorder.common.enums.invoice.customer.InvoiceApplyCustomerInvoiceProgressingStatusEnum;
import cn.superdesk.uniorder.common.enums.order.UnifiedOrderCreditBackAccountStatusEnum;
import cn.superdesk.uniorder.common.numerical.AnStringUtils;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.dao.entity.invoice.customer.InvoiceApply;
import cn.superdesk.uniorder.dto.param.invoice.customer.status.OpBackAccountStatusForm;
import cn.superdesk.uniorder.dto.param.invoice.customer.status.OpChangeStatusBaseItem;
import cn.superdesk.uniorder.executor.scheduler.kafka._thirdpart.qchjc.QchjcStatementKafkaEventPublisher;
import cn.superdesk.uniorder.executor.scheduler.kafka._thirdpart.qchjc.dto.accounting.customer.QchjcCustomerStatementEvent;
import cn.superdesk.uniorder.executor.scheduler.kafka._thirdpart.qchjc.dto.accounting.customer.QchjcCustomerStatementEventMultipleStructVo;
import cn.superdesk.uniorder.executor.scheduler.kafka._thirdpart.qchjc.dto.accounting.customer.QchjcCustomerStatementEventSingleStructVo;
import cn.superdesk.uniorder.pattern.state.invoice.customer.invoiceApply.backAccountStatus.context.InvoiceApplyFieldBackAccountStatusContext;
import cn.superdesk.uniorder.service.order.base.UnifiedOrderCreditService;
import cn.superdesk.uniorder.service.invoice.customer.InvoiceApplyCustomerService;
import cn.superdesk.uniorder.service.invoice.customer.state.InvoiceApplyCustomerFieldBackAccountStatusService;
import cn.superdesk.uniorder.service.invoice.customer.InvoiceApplyXGoodsService;
import cn.superdesk.uniorder.service.thirdpart.szhtxx.electronicInvoice.SzhtxxElectronicInvoiceFptxxExtendInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: 简易状态机 星型结构
 * @Author: linjinyu
 * @CreateDate: 10/12/20 4:28 PM
 * @UpdateUser: linjinyu
 * @UpdateDate: 10/12/20 4:28 PM
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Slf4j
@Service
public class InvoiceApplyCustomerFieldBackAccountStatusServiceImpl implements InvoiceApplyCustomerFieldBackAccountStatusService {

    @Autowired
    private InvoiceApplyCustomerService invoiceApplyCustomerService;
    @Autowired
    private InvoiceApplyXGoodsService   invoiceApplyXGoodsService;

    @Autowired
    private SzhtxxElectronicInvoiceFptxxExtendInfoService szhtxxElectronicInvoiceFptxxExtendInfoService;

    @Autowired
    private UnifiedOrderCreditService unifiedOrderCreditService;

    /**************************************** superdesk-uniorder-executor start ****************************************/

    @Autowired
    private QchjcStatementKafkaEventPublisher qchjcStatementEventPublisher;

    /**************************************** superdesk-uniorder-executor   end ****************************************/

    /****************************************** 状态机 公共方法 机内使用 实际流转 重用 start ******************************************/

    // 状态流转为 未回款(0, "未回款")
    public void opInitByStateMachine(InvoiceApply entity, LoginSysUser loginSysUser) {

        InvoiceApply forUpdate = new InvoiceApply();
        forUpdate.setId(entity.getId());

        // TODO 更新状态逻辑...

        forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.未回款.getKey());
        forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
        forUpdate.setBackAccountTime(DateUtil.date());

        forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
        forUpdate.setUpdateTime(DateUtil.date());

        invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);
    }

    // 状态流转为 部分回款(1, "部分回款")
    public void opPartByStateMachine(InvoiceApply entity, LoginSysUser loginSysUser) {

        InvoiceApply forUpdate = new InvoiceApply();
        forUpdate.setId(entity.getId());

        // TODO 更新状态逻辑...

        forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.部分回款.getKey());
        forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
        forUpdate.setBackAccountTime(DateUtil.date());

        forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
        forUpdate.setUpdateTime(DateUtil.date());

        invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);
    }

    @Override
    public int opRedWriteByStateMachine(String invoiceApplySn, LoginSysUser loginSysUser) {

        int ret = 0;

        InvoiceApply entity = invoiceApplyCustomerService.findByInvoiceApplySn(invoiceApplySn, loginSysUser);

        ret = opRedWriteByStateMachine(entity, loginSysUser);

        return ret;
    }

    @Override
    public int opRedWriteByStateMachine(InvoiceApply entity, LoginSysUser loginSysUser) {

        int ret = 0;


        DateTime now = DateUtil.date();

        InvoiceApply forUpdate = new InvoiceApply();
        forUpdate.setId(entity.getId());

        // TODO 更新状态逻辑...

        forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.发票红冲.getKey());
        forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
        forUpdate.setBackAccountTime(now);

        forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
        forUpdate.setUpdateTime(now);

        ret = invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);

        return ret;

    }

    @Override
    public int opBecomeInvalidByStateMachine(String invoiceApplySn, String becomeInvalidReason, LoginSysUser loginSysUser) {

        int ret = 0;

        InvoiceApply entity = invoiceApplyCustomerService.findByInvoiceApplySn(invoiceApplySn, loginSysUser);

        ret = opBecomeInvalidByStateMachine(entity, becomeInvalidReason, loginSysUser);

        return ret;
    }

    @Override
    public int opBecomeInvalidByStateMachine(InvoiceApply entity, String becomeInvalidReason, LoginSysUser loginSysUser) {

        int ret = 0;

        DateTime now = DateUtil.date();

        InvoiceApply forUpdate = new InvoiceApply();
        forUpdate.setId(entity.getId());

        // TODO 更新状态逻辑...

        forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.发票作废.getKey());
        forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
        forUpdate.setBackAccountTime(now);

        /**************************************** 20220509 start ****************************************/

        // 界面上 积分结算->发票申请管理->列表->Remark是开票备注

        // 【【结算中心-测试环境】积分结算-金税盘发票-【发票作废】"作废原因"填充至发票申请的“开票备注”中会多一个“null”】
        // https://www.tapd.cn/33003868/bugtrace/bugs/view?bug_id=1133003868001005081
        forUpdate.setRemark(StrUtil.format("{}{}", AnStringUtils.getString(forUpdate.getRemark()), becomeInvalidReason));


        /**************************************** 20220509 start ****************************************/

        // 界面上 积分结算->金额盘发票->列表->Remark2备注

        //【2. 积分结算-2.4 金税盘发票-2.4.5 【发票作废】-已整理-20220425】
        //  https://www.tapd.cn/33003868/prong/stories/view/1133003868001004194
        //  ·将作废原因内容覆盖填充到该发票的备注中，非发票备注；
        forUpdate.setRemark2(becomeInvalidReason);

        /**************************************** 20220509 start ****************************************/

        forUpdate.setInvoiceStatus(InvoiceApplyCustomerInvoiceProgressingStatusEnum.已作废.getKey());
        forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
        forUpdate.setUpdateTime(now);

        ret = invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);

        return ret;

    }

    // 状态流转为 已回款(2, "已回款")
    public void opCompleteByStateMachine(InvoiceApply entity, LoginSysUser loginSysUser) {

        InvoiceApply forUpdate = new InvoiceApply();
        forUpdate.setId(entity.getId());

        // TODO 更新状态逻辑...

        forUpdate.setInvoiceStatus(InvoiceApplyCustomerInvoiceProgressingStatusEnum.已完成.getKey());
        forUpdate.setInvoiceProgressingUserId(loginSysUser.getSysUserId());
        forUpdate.setInvoiceProgressingTime(DateUtil.date());

        forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.已回款.getKey());
        forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
        forUpdate.setBackAccountTime(DateUtil.date());

        forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
        forUpdate.setUpdateTime(DateUtil.date());

        invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);
    }

    // 状态流转为 已回款(2, "已回款")
    public void opCompleteByStateMachine(List<InvoiceApply> entitys, OpBackAccountStatusForm form, LoginSysUser loginSysUser) {

        /**
         *  key invoiceApplyId
         */
        Map<Long, OpChangeStatusBaseItem> shortcut = form.getSubmitList().stream().collect(Collectors.toMap(OpChangeStatusBaseItem::getInvoiceApplyId, v -> v, (k, v) -> v));

        DateTime now = DateUtil.date();

        entitys.forEach(entity -> {

            InvoiceApply forUpdate = new InvoiceApply();
            forUpdate.setId(entity.getId());

            // TODO 更新状态逻辑...
            forUpdate.setInvoiceStatus(InvoiceApplyCustomerInvoiceProgressingStatusEnum.已完成.getKey());
            forUpdate.setInvoiceProgressingUserId(loginSysUser.getSysUserId());
            forUpdate.setInvoiceProgressingTime(now);


            forUpdate.setBackAccountStatus(InvoiceApplyCustomerBackAccountStatusEnum.已回款.getKey());
            forUpdate.setBackAccountUserId(loginSysUser.getSysUserId());
            forUpdate.setBackAccountTime(now);

            forUpdate.setUpdateUserId(loginSysUser.getSysUserId());
            forUpdate.setUpdateTime(now);

            invoiceApplyCustomerService.updateInvoiceApplyByPrimaryKeySelective(forUpdate);


        });

        // BeRelated 联级更新 trigger
        // 1. 更新 流水's 回款状态
        // 2. 更新 金税盘发票 扩展信息中的 更新人 更新时间 (不会吧,不会要求与回款登记时间一致吧?怕了,还是一致吧)
        processTailRelatedUpdate(entitys, loginSysUser, now);

        /**************************************** 触发事件点 start ****************************************/

        // 触发事件点;集采账单状态推送 TODO
        // 收钱后 ~
        // 触发事件点;
        // 1. 来源于“企业商城”的对账单在**“积分结算-发票管理-【回款登记】”成功时**：若该对账单的申请状态为“全部申请”**&**所有有效的开票申请的申请状态均为“已回款”时；
//        List<QchjcCustomerStatementEventSingleStructVo> eventSingleStructVoList = AnBeanUtils.copy(form.getSubmitList(), QchjcCustomerStatementEventSingleStructVo.class);
        List<QchjcCustomerStatementEventSingleStructVo> eventSingleStructVoList = new ArrayList<>();
        form.getSubmitList().forEach(item -> {
            eventSingleStructVoList.add(
                    QchjcCustomerStatementEventSingleStructVo
                            .builder()

                            .invoiceApplyId(item.getInvoiceApplyId())
                            .invoiceApplySn(item.getInvoiceApplySn())
                            .accountingOrderId(item.getAccountingOrderId())
                            .accountingOrderSn(item.getAccountingOrderSn())

                            .build()
            );
        });

        qchjcStatementEventPublisher.customerStatementEventReceivePaymentStatusSync(
                QchjcCustomerStatementEvent
                        .builder()
                        .msg("积分结算v2.3-发票管理-op寄票登记-op确认寄票(发票种类::不限)(开票方式::不限)")

                        .multipleStructVo(
                                QchjcCustomerStatementEventMultipleStructVo
                                        .builder()
                                        .items(eventSingleStructVoList)
                                        .build()
                        )

                        .loginSysUser(JSONUtil.toJsonStr(loginSysUser))
                        .touchTime(now)

                        .build()
        );

        /**************************************** 触发事件点   end ****************************************/
    }

    public int processTailRelatedUpdate(List<InvoiceApply> entitysFromDb, LoginSysUser loginSysUser, DateTime now) {

        entitysFromDb.forEach(entityFromDb -> {

            int ret = processTailRelatedUpdate(entityFromDb, loginSysUser, now);
        });

        return 1;
    }

    /**
     * 关联更新 单个状态 - 更新 流水's 回款状态
     *
     * @param entityFromDb
     * @param loginSysUser
     * @param now
     * @return
     */
    public int processTailRelatedUpdate(InvoiceApply entityFromDb, LoginSysUser loginSysUser, DateTime now) {

        int retUnifiedOrderCreditFieldBackAccountStatus = 0;

        int retUpdateUpdateUserInfo = 0;


        List<Long> processUnifiedOrderCreditIds = invoiceApplyXGoodsService.findCreditSetByInvoicApplyId(entityFromDb.getId());

        // 1. 更新 流水's 回款状态
        retUnifiedOrderCreditFieldBackAccountStatus = unifiedOrderCreditService.updateFieldBackAccountStatusByUnifiedOrderCreditIds(processUnifiedOrderCreditIds, UnifiedOrderCreditBackAccountStatusEnum.已回款);

        // 2. 更新 金税盘发票 扩展信息中的 更新人 更新时间
        retUpdateUpdateUserInfo = szhtxxElectronicInvoiceFptxxExtendInfoService.updateUpdateUserInfo(entityFromDb.getInvoiceApplySn(), loginSysUser, now);

        return 1;
    }

    /****************************************** 状态机 公共方法 机内使用 实际流转 重用   end ******************************************/


    /****************************************** 状态机 外部使用 类门面 start ******************************************/

    // 状态流转为 未回款(0, "未回款")
    public void opInit(InvoiceApply entity, LoginSysUser loginSysUser) {
        InvoiceApplyFieldBackAccountStatusContext context = new InvoiceApplyFieldBackAccountStatusContext(entity, loginSysUser);
        context.opInit();
    }

    // 状态流转为 部分回款(1, "部分回款")
    public void opPart(InvoiceApply entity, LoginSysUser loginSysUser) {
        InvoiceApplyFieldBackAccountStatusContext context = new InvoiceApplyFieldBackAccountStatusContext(entity, loginSysUser);
        context.opPart();
    }

    // 状态流转为 已回款(2, "已回款")
    public void opComplete(InvoiceApply entity, LoginSysUser loginSysUser) {
        InvoiceApplyFieldBackAccountStatusContext context = new InvoiceApplyFieldBackAccountStatusContext(entity, loginSysUser);
        context.opComplete();
    }

    // 状态流转为 已回款(2, "已回款")
    public void opComplete(List<InvoiceApply> entitys, OpBackAccountStatusForm form, LoginSysUser loginSysUser) {
        InvoiceApplyFieldBackAccountStatusContext context = new InvoiceApplyFieldBackAccountStatusContext(entitys, form, loginSysUser);
        context.opComplete();
    }

    /****************************************** 状态机 外部使用 类门面   end ******************************************/
}
