package com.ecep.contract.cloud.u8.ctx;

import static com.ecep.contract.SpringApp.getBean;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.hibernate.Hibernate;
import org.springframework.data.domain.Sort;

import com.ecep.contract.MessageHolder;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.service.PurchaseOrderItemService;
import com.ecep.contract.ds.contract.service.PurchaseOrdersService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.PurchaseOrder;
import com.ecep.contract.model.PurchaseOrderItem;
import com.ecep.contract.util.NumberUtils;

import lombok.Setter;

public class PurchaseOrderCtx extends AbstractYongYouU8Ctx {
    @Setter
    private ContractService contractService;
    @Setter
    private PurchaseOrdersService purchaseOrdersService;
    @Setter
    private PurchaseOrderItemService purchaseOrderItemService;

    InventoryCtx inventoryCtx;
    CompanyBankAccountCtx companyBankAccountCtx;

    InventoryCtx getInventoryCtx() {
        if (inventoryCtx == null) {
            inventoryCtx = new InventoryCtx();
            inventoryCtx.from(this);
        }
        return inventoryCtx;
    }

    CompanyBankAccountCtx getCompanyBankAccountCtx() {
        if (companyBankAccountCtx == null) {
            companyBankAccountCtx = new CompanyBankAccountCtx();
            companyBankAccountCtx.from(this);
        }
        return companyBankAccountCtx;
    }

    ContractService getContractService() {
        if (contractService == null) {
            contractService = getBean(ContractService.class);
        }
        return contractService;
    }

    PurchaseOrdersService getPurchaseOrdersService() {
        if (purchaseOrdersService == null) {
            purchaseOrdersService = getBean(PurchaseOrdersService.class);
        }
        return purchaseOrdersService;
    }

    PurchaseOrderItemService getPurchaseOrderItemService() {
        if (purchaseOrderItemService == null) {
            purchaseOrderItemService = getBean(PurchaseOrderItemService.class);
        }
        return purchaseOrderItemService;
    }

    public List<PurchaseOrder> syncByContract(Contract contract, MessageHolder holder) {
        PurchaseOrdersService ordersService = getPurchaseOrdersService();
        PurchaseOrderItemService itemService = getPurchaseOrderItemService();

        List<PurchaseOrder> orders = ordersService.findAll((root, q, cb) -> {
            return cb.equal(root.get("contract"), contract);
        }, Sort.unsorted());
        holder.debug("查找到 " + orders.size() + " 条采购订单记录在数据库中");
        Map<Integer, PurchaseOrder> ordersMap = orders.stream()
                .collect(Collectors.toMap(PurchaseOrder::getRefId, item -> item));

        List<PurchaseOrderItem> items = itemService.findAll((root, q, cb) -> {
            return cb.equal(root.get("order").get("contract"), contract);
        }, Sort.unsorted());

        // 按 order 分组
        Map<PurchaseOrder, Map<Integer, PurchaseOrderItem>> itemMap = items.stream()
                .collect(Collectors.groupingBy(PurchaseOrderItem::getOrder,
                        Collectors.toMap(PurchaseOrderItem::getRefId, item -> item)));

        // 查询 U8 数据库
        List<Map<String, Object>> ds = repository.findAllPurchaseOrderItemByContractCode(contract.getCode());
        holder.debug("查找到 " + ds.size() + " 条采购订单条目记录在 " + CloudServiceConstant.U8_NAME);

        Map<PurchaseOrder, List<PurchaseOrderItem>> updateMap = new HashMap<>();
        for (Map<String, Object> map : ds) {
            Integer poId = (Integer) map.get("POID");
            if (poId == 0) {
                holder.warn("跳过无效采购订单记录：缺少 POID");
                continue;
            }
            PurchaseOrder order = ordersMap.get(poId);
            if (order == null) {
                order = new PurchaseOrder();
                order.setContract(contract);
                order.setRefId(poId);

                order = ordersService.save(order);
                ordersMap.put(poId, order);
                holder.info("新增采购订单 #" + poId);
            }
        }
        for (Map<String, Object> map : ds) {
            // holder.debug("条目：" + map.toString());
            Integer poId = (Integer) map.get("POID");
            if (poId == 0) {
                holder.warn("跳过无效采购订单记录：缺少 POID");
                continue;
            }
            PurchaseOrder order = ordersMap.get(poId);

            boolean itemModified = false;
            List<PurchaseOrderItem> updates = updateMap.computeIfAbsent(order, k -> new ArrayList<>());

            // 获取条目标识并处理 null
            Integer refId = (Integer) map.get("ID");
            if (poId == null) {
                holder.warn("跳过条目：订单 " + poId + " 缺少 ID");
                continue;
            }
            Map<Integer, PurchaseOrderItem> subItemMap = itemMap.get(order);
            PurchaseOrderItem item = null;
            if (subItemMap != null) {
                item = subItemMap.remove(refId);
            }

            if (item == null) {
                item = new PurchaseOrderItem();
                item.setOrder(order);
                item.setRefId(refId);
                itemModified = true;

                holder.info("新增采购订单条目 #" + refId);
            }
            MessageHolder subHolder = holder.sub("---| ");
            if (applyPurchaseOrderItemDetail(item, map, subHolder)) {
                itemModified = true;
            }
            if (itemModified) {
                item = itemService.save(item);
            }
            updates.add(item);
        }

        for (PurchaseOrder order : updateMap.keySet()) {
            if (applyPurchaseOrderDetail(order, repository.queryPurchaseOrderDetail(order.getRefId()), holder)) {
                ordersService.save(order);
            }
        }

        if (!itemMap.isEmpty()) {
            for (Map<Integer, PurchaseOrderItem> subMap : itemMap.values()) {
                if (!subMap.isEmpty()) {
                    for (PurchaseOrderItem item : subMap.values()) {
                        holder.info("删除采购订单条目");
                        itemService.delete(item);
                    }
                }
            }

            for (PurchaseOrder order : updateMap.keySet()) {
                itemMap.remove(order);
            }
            if (!itemMap.isEmpty()) {
                holder.info("剩余 " + itemMap.size() + " 个采购订单条目");
            }
        }

        return new ArrayList<>(updateMap.keySet());
    }

    private boolean applyPurchaseOrderDetail(PurchaseOrder order, Map<String, Object> map, MessageHolder holder) {
        String code = (String) map.get("cPOID");

        String venBank = (String) map.get("cVenBank");
        String venBankAccount = (String) map.get("cVenAccount");

        String venCode = (String) map.get("cVenCode");
        String memo = (String) map.get("cMemo");

        boolean modified = false;

        if (!Objects.equals(order.getCode(), code)) {
            order.setCode(code);
            holder.info("订单编号更新为 " + code);
            modified = true;
        }

        if (updateEmployeeByCode(order::getEmployee, order::setEmployee, (String) map.get("cPersonCode"), holder,
                "业务员")) {
            modified = true;
        }
        if (updateEmployeeByName(order::getMaker, order::setMaker, (String) map.get("cMaker"), holder, "制单人")) {
            modified = true;
        }
        if (updateEmployeeByName(order::getVerifier, order::setVerifier, (String) map.get("cVerifier"), holder,
                "审核人")) {
            modified = true;
        }
        if (updateEmployeeByName(order::getCloser, order::setCloser, (String) map.get("cCloser"), holder, "订单关闭人")) {
            modified = true;
        }
        if (updateLocalDateTime(order::getMakerDate, order::setMakerDate, (Timestamp) map.get("cmaketime"), holder,
                "制单日期")) {
            modified = true;
        }
        if (updateLocalDateTime(order::getModifyDate, order::setModifyDate, (Timestamp) map.get("cModifyTime"), holder,
                "修改日期")) {
            modified = true;
        }
        if (updateLocalDateTime(order::getVerifierDate, order::setVerifierDate, (Timestamp) map.get("cAuditTime"),
                holder, "审核日期")) {
            modified = true;
        }
        if (updateLocalDateTime(order::getCloserDate, order::setCloserDate, (Timestamp) map.get("dCloseTime"), holder,
                "关闭日期")) {
            modified = true;
        }
        if (!Objects.equals(order.getVendorCode(), venCode)) {
            order.setVendorCode(venCode);
            holder.info("供方代码修改为: " + venCode);
            modified = true;
        }

        if (!Objects.equals(order.getDescription(), memo)) {
            order.setDescription(memo);
            holder.info("描述修改为: " + memo);
            modified = true;
        }

        updateCompanyBankAccount(order.getContract(), venBank, venBankAccount, holder);

        return modified;
    }

    private void updateCompanyBankAccount(Contract contract, String bank, String bankAccount, MessageHolder holder) {
        if (contract == null) {
            return;
        }
        if (!Hibernate.isInitialized(contract)) {
            contract = getContractService().findById(contract.getId());
        }
        getCompanyBankAccountCtx().updateBankAccount(contract.getCompany(), bank, bankAccount, holder);
    }

    private boolean applyPurchaseOrderItemDetail(PurchaseOrderItem item, Map<String, Object> map,
            MessageHolder holder) {
        Integer refId = (Integer) map.get("ID");
        String inventoryCode = (String) map.get("cInvCode");
        String contractCode = (String) map.get("ContractCode");

        String title = (String) map.get("cInvCode");
        double quantity = (double) map.get("iQuantity");
        BigDecimal taxRate = (BigDecimal) map.get("iPerTaxRate");
        BigDecimal taxPrice = (BigDecimal) map.get("iTaxPrice");
        double exclusiveTaxPrice = (double) map.get("iUnitPrice");
        BigDecimal amount = (BigDecimal) map.get("iSum");

        Timestamp arriveDate = (Timestamp) map.get("dArriveDate");

        boolean modified = false;

        holder.debug("条目：" + title + " x " + amount);
        if (updateInventory(item::getInventory, item::setInventory, inventoryCode, holder, "商品")) {
            modified = true;
        }

        if (!Objects.equals(item.getRefId(), refId)) {
            item.setRefId(refId);
            holder.info("RefId修改为: " + refId);
            modified = true;
        }
        if (!NumberUtils.equals(item.getExclusiveTaxPrice(), exclusiveTaxPrice)) {
            item.setExclusiveTaxPrice(exclusiveTaxPrice);
            holder.info("不含税单价修改为: " + exclusiveTaxPrice);
            modified = true;
        }

        if (!NumberUtils.equals(item.getPrice(), taxPrice.doubleValue())) {
            item.setPrice(taxPrice.doubleValue());
            holder.info("含税单价修改为: " + taxPrice.doubleValue());
            modified = true;
        }
        if (!NumberUtils.equals(item.getTaxRate(), taxRate.doubleValue())) {
            item.setTaxRate(taxRate.doubleValue());
            holder.info("税率修改为: " + taxRate);
            modified = true;
        }
        if (!NumberUtils.equals(item.getQuantity(), quantity)) {
            item.setQuantity(quantity);
            holder.info("数量修改为: " + quantity);
            modified = true;
        }

        if (updateLocalDate(item::getArriveDate, item::setArriveDate, (Timestamp) map.get("dArriveDate"), holder,
                "开始日期")) {
            modified = true;
        }

        if (!Objects.equals(item.getDescription(), contractCode)) {
            item.setDescription(contractCode);
            holder.info("描述修改为: " + contractCode);
            modified = true;
        }

        return modified;
    }

    boolean updateInventory(Supplier<Inventory> getter, Consumer<Inventory> setter, String inventoryCode,
            MessageHolder holder, String topic) {
        return getInventoryCtx().syncInventoryDetailByCode(getter, setter, inventoryCode, holder, topic);
    }
}
