package com.ecep.contract.ds.contract.tasker;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import org.hibernate.Hibernate;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.util.StringUtils;

import com.ecep.contract.ContractPayWay;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.cloud.u8.YongYouU8Service;
import com.ecep.contract.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.cloud.u8.ctx.PurchaseBillVoucherCtx;
import com.ecep.contract.cloud.u8.ctx.PurchaseOrderCtx;
import com.ecep.contract.cloud.u8.ctx.SalesBillVoucherCtx;
import com.ecep.contract.cloud.u8.ctx.SalesOrderCtx;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.SalesOrder;
import com.ecep.contract.ui.Tasker;

import lombok.Getter;

public abstract class AbstContractRepairTasker extends Tasker<Object> {
    @Getter
    protected boolean filesUpdated = false;
    @Getter
    protected boolean itemsUpdated = false;
    @Getter
    protected boolean playPlanUpdated = false;
    @Getter
    protected boolean saleOrderUpdated = false;

    protected final ContractCtx contractCtx = new ContractCtx();
    protected SalesOrderCtx salesOrderCtx = null;
    protected SalesBillVoucherCtx salesBillVoucherCtx = null;
    protected PurchaseOrderCtx purchaseOrderCtx = null;
    protected PurchaseBillVoucherCtx purchaseBillVoucherCtx = null;

    SalesOrderCtx getSalesOrderCtx() {
        if (salesOrderCtx == null) {
            salesOrderCtx = new SalesOrderCtx();
            salesOrderCtx.from(contractCtx);
        }
        return salesOrderCtx;
    }

    SalesBillVoucherCtx getSalesBillVoucherCtx() {
        if (salesBillVoucherCtx == null) {
            salesBillVoucherCtx = new SalesBillVoucherCtx();
            salesBillVoucherCtx.from(contractCtx);
        }
        return salesBillVoucherCtx;
    }

    PurchaseOrderCtx getPurchaseOrderCtx() {
        if (purchaseOrderCtx == null) {
            purchaseOrderCtx = new PurchaseOrderCtx();
            purchaseOrderCtx.from(contractCtx);
        }
        return purchaseOrderCtx;
    }

    PurchaseBillVoucherCtx getPurchaseBillVoucherCtx() {
        if (purchaseBillVoucherCtx == null) {
            purchaseBillVoucherCtx = new PurchaseBillVoucherCtx();
            purchaseBillVoucherCtx.from(contractCtx);
        }
        return purchaseBillVoucherCtx;
    }

    public void setContractService(ContractService contractService) {
        contractCtx.setContractService(contractService);
    }

    @Override
    protected Object execute(MessageHolder holder) throws Exception {
        try {
            YongYouU8Service u8Service = SpringApp.getBean(YongYouU8Service.class);
            u8Service.initialize(contractCtx);
        } catch (NoSuchBeanDefinitionException ignored) {
            holder.warn("无法使用 " + CloudServiceConstant.U8_NAME + " 服务");
        }
        try {
            repair(holder);
        } catch (CannotGetJdbcConnectionException e) {
            throw new RuntimeException("无法获取的数据库连接", e);
        }
        return null;
    }

    protected abstract void repair(MessageHolder holder);

    /**
     * rs 数据从 CM_List 表中读取
     */
    protected boolean repairFromCMList(Map<String, Object> rs, MessageHolder holder) {
        String contractID = (String) rs.get("strContractID");
        String guid = (String) rs.get("GUID");
        String contractName = (String) rs.get("strContractName");
        String state = (String) rs.get("strState");

        holder.info("同步合同 " + contractID + " " + contractName);
        Contract contract = contractCtx.findOrCreatByGuidOrContractId(guid, contractID, holder);
        if (contract == null) {
            holder.warn("未知合同" + contractID + ", " + rs.get("strContractName"));
            return false;
        }

        boolean modified = false;
        if (contractCtx.updateText(contract::getCode, contract::setCode, contractID, holder, "合同编号")) {
            ContractService contractService = contractCtx.getContractService();
            Contract dup = contractService.findByCode(contractID);
            if (dup != null) {
                if (!Objects.equals(contract, dup)) {
                    contractService.delete(dup);
                    holder.warn("删除重复的合同 " + dup);
                }
            }
            modified = true;
        }
        if (contractCtx.updateText(contract::getGuid, contract::setGuid, guid, holder, "GUID")) {
            ContractService contractService = contractCtx.getContractService();
            Contract dup = contractService.findByGuid(guid);
            if (dup != null) {
                if (!Objects.equals(contract, dup)) {
                    contractService.delete(dup);
                    holder.warn("删除重复的合同 " + dup);
                }
            }
            modified = true;
        }

        if (contractCtx.updateText(contract::getState, contract::setState, state, holder, "合同状态")) {
            modified = true;
        }

        if (modified) {
            contract = contractCtx.save(contract);
        }

        Contract parent = null;

        if (contract.getPayWay() == ContractPayWay.PAY) {
            if (StringUtils.hasText(contract.getParentCode())) {
                parent = contractCtx.findContractByCode(contract.getParentCode());
            }
        }
        AtomicReference<Contract> refer = new AtomicReference<>(contract);
        try {
            return repair(refer, parent, holder, progress -> {
            });
        } catch (Exception e) {
            holder.error(state);
            return false;
        }
    }

    /**
     * @param contractProperty
     * @param holder
     * @param progress
     * @return
     */
    public boolean repair(
            AtomicReference<Contract> contractProperty,
            Contract parent, MessageHolder holder, Consumer<Double> progress) {
        boolean repaired = false;
        boolean modified = false;
        Contract contract = contractProperty.get();

        progress.accept(0.1);
        if (contractCtx.updateContractDetailByGuid(contract, contract.getGuid(), holder)) {
            modified = true;
        }
        progress.accept(0.2);
        if (contractCtx.updateContractExec(contract, holder)) {
            modified = true;
        }
        progress.accept(0.3);
        if (contractCtx.updateContractAmount(contract, holder)) {
            modified = true;
        }
        progress.accept(0.4);
        if (contractCtx.updateParentContract(contract, holder)) {
            modified = true;
        }
        if (contractCtx.updateContractPath(contract, holder)) {
            modified = true;
        }
        progress.accept(0.5);
        if (modified) {
            contract = contractCtx.save(contract);
            contractProperty.set(contract);
            modified = false;
            repaired = true;
        }
        progress.accept(0.6);
        if (contractCtx.syncContractItems(contract, holder)) {
            itemsUpdated = true;
            modified = true;
        }
        progress.accept(0.66);
        if (contractCtx.syncContractPayPlan(contract, holder)) {
            playPlanUpdated = true;
        }
        progress.accept(0.7);
        if (contractCtx.updateContractProject(contract, parent, holder)) {
            modified = true;
        }
        if (modified) {
            contract = contractCtx.save(contract);
            contractProperty.set(contract);
            modified = false;
            repaired = true;
        }
        progress.accept(0.8);
        // 销售合同
        if (contract.getPayWay() == ContractPayWay.RECEIVE) {
            syncSaleOrder(contract, holder);
            syncChildrenContract(contract, holder, p -> progress.accept(p * 0.1 + 0.8));

        } else if (contract.getPayWay() == ContractPayWay.PAY) {
            syncPurchaseOrder(contract, holder);
        }
        progress.accept(0.9);
        if (contractCtx.syncContractFiles(contract, holder)) {
            filesUpdated = true;
            repaired = true;
        }
        progress.accept(1.0);
        return repaired;
    }

    private void syncChildrenContract(Contract parent, MessageHolder holder, Consumer<Double> progress) {
        ContractService contractService = contractCtx.getContractService();
        List<Contract> list = contractService.findAllByParent(parent);
        for (int i = 0; i < list.size(); i++) {
            Contract v = list.get(i);
            holder.info("子合同 ： " + v.getCode() + " " + v.getName());
            MessageHolder subHolder = holder.sub(" -- ");
            boolean modified = false;
            Project project = parent.getProject();
            if (project == null) {
                project = v.getProject();
            }
            AtomicReference<Contract> contractProperty = new AtomicReference<>(v);
            try {
                repair(contractProperty, parent, subHolder, p -> {
                });

                v = contractProperty.get();
                if (contractCtx.updateContractProject(v, project, subHolder)) {
                    modified = true;
                }
            } catch (Exception e) {
                if (!Hibernate.isInitialized(v)) {
                    v = contractService.findById(v.getId());
                }
                throw new RuntimeException("同步子合同失败, contract=" + v.toPrettyString(), e);
            }
            if (modified) {
                try {
                    v = contractCtx.save(v);
                } catch (Exception e) {
                    throw new RuntimeException("#" + v.getId() + ", v=" + v.getVersion(), e);
                }
                modified = false;
            }
            progress.accept((double) i / list.size());
        }
    }

    /**
     * 同步合同的销售订单
     *
     * @param contract 合同
     * @param holder   消息
     */
    private void syncSaleOrder(Contract contract, MessageHolder holder) {
        SalesOrderCtx ctx = getSalesOrderCtx();
        if (ctx.getRepository() == null) {
            return;
        }
        List<SalesOrder> orders = ctx.syncByContract(contract, holder);
        if (orders == null) {
            return;
        }
        SalesBillVoucherCtx voucherCtx = getSalesBillVoucherCtx();
        for (SalesOrder order : orders) {
            voucherCtx.syncBySalesOrder(order, holder);
        }
    }

    /**
     * 同步合同采购订单
     *
     * @param contract 合同
     * @param holder   消息
     */
    private void syncPurchaseOrder(Contract contract, MessageHolder holder) {
        PurchaseOrderCtx ctx1 = getPurchaseOrderCtx();
        if (ctx1.getRepository() != null) {
            ctx1.syncByContract(contract, holder);
        }

        PurchaseBillVoucherCtx ctx2 = getPurchaseBillVoucherCtx();
        if (ctx2.getRepository() != null) {
            ctx2.syncByContract(contract, holder);
        }
    }
}
