package easydo.technology.service.impl;

import easydo.technology.components.JdbcClient;
import easydo.technology.enums.MESEnum;
import easydo.technology.exception.BizException;
import easydo.technology.model.*;
import easydo.technology.model.vo.*;
import easydo.technology.service.FlowNoService;
import easydo.technology.service.ProductPrePlanService;
import easydo.technology.system.model.SysUser;
import easydo.technology.utils.SecurityUtils;
import easydo.technology.utils.StringUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ProductPrePlanServiceImpl implements ProductPrePlanService {

    @Resource
    private JdbcClient jdbcClient;
    @Resource
    private DataSource dataSource;
    @Resource
    private FlowNoService flowNoService;

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> getPage(Map<String, Object> map) throws Exception {
        // 说明：该接口同步自 Go 端的 productPrePlanGetPage 逻辑，基于销售订单（SaleOrder）维度进行深度级联查询和组装。
        // 注意：这种 1:1 对齐方式涉及多层嵌套循环查询（N+1），在数据量较大时需关注性能。
        Connection connection = dataSource.getConnection();
        try {
            // 1) 查询销售订单分页数据
            Map<String, Object> result = jdbcClient.getJdbcPage(map, SaleOrder.class, connection);
            List<SaleOrder> list = (List<SaleOrder>) result.get("records");
            if (list == null || list.isEmpty()) {
                return result;
            }

            for (SaleOrder model : list) {
                // 2) 查询并回填销售订单附件（Minio 文件）
                jdbcClient.getMinioFile(model, connection);

                // 3) 查询并回填订单明细列表（SaleOrderDetail）
                SaleOrderDetail detailParam = new SaleOrderDetail();
                detailParam.setOrderId(model.getId());
                List<SaleOrderDetail> detailList = jdbcClient.getJdbcList(detailParam, connection);

                if (detailList != null) {
                    for (SaleOrderDetail detail : detailList) {
                        // 3.1) 查询并回填明细对应的物料基础信息（ProductMaterial）
                        ProductMaterial materialParam = new ProductMaterial();
                        materialParam.setCode(detail.getMaterialCode());
                        materialParam.setTenantId(model.getTenantId());
                        ProductMaterial material = jdbcClient.getJdbcModel(materialParam, connection);
                        detail.setMaterial(material);

                        // 3.2) 查询该物料是否存在启用的顶级 BOM（parentId = 0）
                        ProductBom bomParam = new ProductBom();
                        bomParam.setParentId("0");
                        bomParam.setMaterialCode(material.getCode());
                        bomParam.setTenantId(model.getTenantId());
                        bomParam.setStatus(MESEnum.PROCESS_OF_STATUS_ENABLE.getValue());
                        ProductBom topBom = null;
                        try {
                            topBom = jdbcClient.getJdbcModel(bomParam, connection);
                        } catch (Exception ex) {
                            // 若未找到对应的 BOM，则 topBom 保持为 null
                        }

                        if (topBom != null && StringUtil.isNotEmpty(topBom.getId())) {
                            // 3.3) 分支 A：存在 BOM，则按 bomCode 查询出该 BOM 下的所有组成项
                            ProductBom bomListParam = new ProductBom();
                            bomListParam.setBomCode(topBom.getBomCode());
                            bomListParam.setTenantId(model.getTenantId());
                            List<ProductBom> bomList = jdbcClient.getJdbcList(bomListParam, connection);

                            if (bomList != null) {
                                for (ProductBom bomModel : bomList) {
                                    // 3.3.1) 查询并回填 BOM 组成项的物料信息
                                    ProductMaterial bmMaterialParam = new ProductMaterial();
                                    bmMaterialParam.setCode(bomModel.getMaterialCode());
                                    bmMaterialParam.setTenantId(model.getTenantId());
                                    ProductMaterial bmMaterial = jdbcClient.getJdbcModel(bmMaterialParam, connection);
                                    bomModel.setMaterial(bmMaterial);

                                    // 3.3.2) 查询并回填该物料在所有仓库中的可用库存（WarehouseMaterial）
                                    WarehouseMaterial wmParam = new WarehouseMaterial();
                                    wmParam.setMaterialCode(bmMaterial.getCode());
                                    wmParam.setTenantId(model.getTenantId());
                                    wmParam.setStatus(MESEnum.PROCESS_OF_STATUS_ENABLE.getValue());
                                    List<WarehouseMaterial> wmList = jdbcClient.getJdbcList(wmParam, connection);
                                    if (wmList != null) {
                                        for (WarehouseMaterial wm : wmList) {
                                            // 同时回填仓库的基础信息（Warehouse）
                                            Warehouse wParam = new Warehouse();
                                            wParam.setId(wm.getWarehouseId());
                                            wm.setWarehouse(jdbcClient.getJdbcModelById(wParam, connection));
                                        }
                                    }
                                    bomModel.setWarehouseMaterialList(wmList);
                                }
                            }
                            detail.setBomList(bomList);
                        } else {
                            // 3.4) 分支 B：不存在 BOM，则直接查询该明细物料的可用库存信息
                            WarehouseMaterial wmParam = new WarehouseMaterial();
                            wmParam.setMaterialCode(material.getCode());
                            wmParam.setTenantId(model.getTenantId());
                            wmParam.setStatus(MESEnum.PROCESS_OF_STATUS_ENABLE.getValue());
                            List<WarehouseMaterial> wmList = jdbcClient.getJdbcList(wmParam, connection);
                            if (wmList != null) {
                                for (WarehouseMaterial wm : wmList) {
                                    Warehouse wParam = new Warehouse();
                                    wParam.setId(wm.getWarehouseId());
                                    wm.setWarehouse(jdbcClient.getJdbcModelById(wParam, connection));
                                }
                            }
                            detail.setWarehouseMaterialList(wmList);
                        }
                    }
                }

                // 4) 将组装好的明细列表回填给订单
                model.setChildrenList(detailList);

                // 5) 查询并回填负责人姓名（来自 sys_user）
                if (model.getManagerId() != null && model.getManagerId() != 0) {
                    SysUser managerParam = new SysUser();
                    managerParam.setId(model.getManagerId());
                    SysUser manager = jdbcClient.getJdbcModelById(managerParam, connection);
                    if (manager != null) {
                        model.setManagerName(manager.getNickName());
                    }
                }

                // 6) 查询并回填客户名称（来自 customer 表）
                if (StringUtil.isNotEmpty(model.getCustomerId())) {
                    Customer customerParam = new Customer();
                    customerParam.setId(model.getCustomerId());
                    Customer customer = jdbcClient.getJdbcModelById(customerParam, connection);
                    if (customer != null) {
                        model.setCustomerName(customer.getName());
                    }
                }
            }
            return result;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        } finally {
            if (connection != null) connection.close();
        }
    }

    @Override
    public ProductPrePlanVO save(ProductPrePlanVO vo) throws Exception {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        try {
            // 获取当前登录用户 ID，与 Go 版本从 session 取 user_id 逻辑对齐
            Long userId = SecurityUtils.getCurrentUserId();

            // 1. 更新销售订单状态为“备货中” (processing)
            SaleOrder saleOrder = new SaleOrder();
            saleOrder.setId(vo.getSaleOrderId());
            saleOrder = jdbcClient.getJdbcModelById(saleOrder, connection);
            if (saleOrder == null) throw new BizException("销售订单不存在");
            
            saleOrder.setStatus(MESEnum.SALE_ORDER_OF_STATUS_PROCESSING.getValue());
            jdbcClient.jdbcUpdateById(saleOrder, connection);

            // 2. 处理生产计划 (ProductPlan)
            if (vo.getProductPlanVoList() != null && !vo.getProductPlanVoList().isEmpty()) {
                List<ProductPlanVO> planFilterList = vo.getProductPlanVoList().stream()
                        .filter(item -> item.getPlanProductNumber() != null && item.getPlanProductNumber().doubleValue() > 0)
                        .collect(Collectors.toList());
                
                if (!planFilterList.isEmpty()) {
                    ProductPlan productPlan = new ProductPlan();
                    productPlan.setTenantId(saleOrder.getTenantId());
                    productPlan.setSaleOrderId(saleOrder.getId());
                    
                    // 生成生产计划流水号
                    String code = flowNoService.generateProductPlanCode(productPlan, connection);
                    productPlan.setCode(code);
                    productPlan.setName(StringUtil.isNotEmpty(vo.getProductPlanName()) ? vo.getProductPlanName() : code);
                    productPlan.setStatus(MESEnum.PRODUCT_PLAN_OF_STATUS_PENDING.getValue());
                    productPlan.setBeginDate(vo.getPlanBeginDate());
                    productPlan.setEndDate(vo.getPlanEndDate());
                    productPlan.setCreateId(userId);
                    
                    jdbcClient.jdbcInsert(productPlan, connection);

                    // 插入生产计划 BOM 明细 (ProductPlanBom)
                    for (ProductPlanVO detailVo : planFilterList) {
                        ProductPlanBom planBom = new ProductPlanBom();
                        planBom.setPlanId(productPlan.getId());
                        planBom.setBomId(detailVo.getBomId());
                        // 修复类型不匹配：BigDecimal 转 Double
                        planBom.setNumber(detailVo.getPlanProductNumber() == null ? null : detailVo.getPlanProductNumber().doubleValue());

                        // 查找 BOM 关联的工艺路线和质检方案
                        ProductBom bom = new ProductBom();
                        bom.setId(detailVo.getBomId());
                        bom = jdbcClient.getJdbcModelById(bom, connection);
                        if (bom != null) {
                            planBom.setRouteId(bom.getRouteId());
                            ProcessRoute route = new ProcessRoute();
                            route.setId(bom.getRouteId());
                            route = jdbcClient.getJdbcModelById(route, connection);
                            if (route != null) {
                                planBom.setInspectProgramId(route.getInspectProgramId());
                            }
                        }
                        jdbcClient.jdbcInsert(planBom, connection);
                    }

                    // 处理库存锁定 (WarehouseRecord + WarehouseMaterial)
                    for (ProductPlanVO detailVo : vo.getProductPlanVoList()) {
                        if (detailVo.getWarehouseMaterialVoList() != null) {
                            lockWarehouse(detailVo.getWarehouseMaterialVoList(), detailVo.getMaterialCode(), 
                                    MESEnum.WAREHOUSE_RECORD_OF_REF_TYPE_PRODUCT.getValue(), productPlan.getId(), 
                                    saleOrder, userId, connection);
                        }
                    }
                }
            }

            // 3. 处理采购计划 (PurchasePlan)
            if (vo.getPurchasePlanVoList() != null && !vo.getPurchasePlanVoList().isEmpty()) {
                List<PurchasePlanVO> planFilterList = vo.getPurchasePlanVoList().stream()
                        .filter(item -> item.getPlanPurchaseNumber() != null && item.getPlanPurchaseNumber().doubleValue() > 0)
                        .collect(Collectors.toList());

                if (!planFilterList.isEmpty()) {
                    PurchasePlan purchasePlan = new PurchasePlan();
                    purchasePlan.setTenantId(saleOrder.getTenantId());
                    purchasePlan.setSaleOrderId(saleOrder.getId());
                    
                    String code = flowNoService.generatePurchasePlanCode(purchasePlan, connection);
                    purchasePlan.setCode(code);
                    purchasePlan.setName(StringUtil.isNotEmpty(vo.getPurchasePlanName()) ? vo.getPurchasePlanName() : code);
                    purchasePlan.setStatus(MESEnum.PURCHASE_PLAN_OF_STATUS_PENDING.getValue());
                    purchasePlan.setBeginDate(vo.getPlanBeginDate());
                    purchasePlan.setEndDate(vo.getPlanEndDate());
                    purchasePlan.setCreateId(userId);
                    
                    jdbcClient.jdbcInsert(purchasePlan, connection);

                    for (PurchasePlanVO detailVo : planFilterList) {
                        PurchasePlanDetail detail = new PurchasePlanDetail();
                        detail.setPlanId(purchasePlan.getId());
                        detail.setMaterialCode(detailVo.getMaterialCode());
                        detail.setNumber(detailVo.getPlanPurchaseNumber());
                        jdbcClient.jdbcInsert(detail, connection);
                    }

                    for (PurchasePlanVO detailVo : vo.getPurchasePlanVoList()) {
                        if (detailVo.getWarehouseMaterialVoList() != null) {
                            lockWarehouse(detailVo.getWarehouseMaterialVoList(), detailVo.getMaterialCode(), 
                                    MESEnum.WAREHOUSE_RECORD_OF_REF_TYPE_PURCHASE.getValue(), purchasePlan.getId(), 
                                    saleOrder, userId, connection);
                        }
                    }
                }
            }

            // 4. 处理委外计划 (OutsourcingPlan)
            if (vo.getOutsourcingPlanVoList() != null && !vo.getOutsourcingPlanVoList().isEmpty()) {
                List<OutsourcingPlanVO> planFilterList = vo.getOutsourcingPlanVoList().stream()
                        .filter(item -> item.getPlanOutsourcingNumber() != null && item.getPlanOutsourcingNumber().doubleValue() > 0)
                        .collect(Collectors.toList());

                if (!planFilterList.isEmpty()) {
                    OutsourcingPlan outsourcingPlan = new OutsourcingPlan();
                    outsourcingPlan.setTenantId(saleOrder.getTenantId());
                    outsourcingPlan.setSaleOrderId(saleOrder.getId());
                    
                    String code = flowNoService.generateOutsourcingPlanCode(outsourcingPlan, connection);
                    outsourcingPlan.setCode(code);
                    outsourcingPlan.setName(StringUtil.isNotEmpty(vo.getOutsourcingPlanName()) ? vo.getOutsourcingPlanName() : code);
                    outsourcingPlan.setStatus(MESEnum.OUTSOURCING_PLAN_OF_STATUS_PENDING.getValue());
                    outsourcingPlan.setType(MESEnum.OUTSOURCING_PLAN_OF_TYPE_WHOLE.getValue());
                    outsourcingPlan.setBeginDate(vo.getPlanBeginDate());
                    outsourcingPlan.setEndDate(vo.getPlanEndDate());
                    outsourcingPlan.setCreateId(userId);
                    
                    jdbcClient.jdbcInsert(outsourcingPlan, connection);

                    for (OutsourcingPlanVO detailVo : planFilterList) {
                        OutsourcingPlanDetail detail = new OutsourcingPlanDetail();
                        detail.setPlanId(outsourcingPlan.getId());
                        detail.setMaterialCode(detailVo.getMaterialCode());
                        detail.setNumber(detailVo.getPlanOutsourcingNumber());
                        jdbcClient.jdbcInsert(detail, connection);
                    }

                    for (OutsourcingPlanVO detailVo : vo.getOutsourcingPlanVoList()) {
                        if (detailVo.getWarehouseMaterialVoList() != null) {
                            lockWarehouse(detailVo.getWarehouseMaterialVoList(), detailVo.getMaterialCode(), 
                                    MESEnum.WAREHOUSE_RECORD_OF_REF_TYPE_OUTSOURCING.getValue(), outsourcingPlan.getId(), 
                                    saleOrder, userId, connection);
                        }
                    }
                }
            }

            connection.commit();
            return vo;
        } catch (Exception e) {
            connection.rollback();
            throw new BizException(e.getMessage());
        } finally {
            if (connection != null) connection.close();
        }
    }

    /**
     * 内部方法：执行库存锁定逻辑 (WarehouseRecord + 更新 WarehouseMaterial)
     */
    private void lockWarehouse(List<WarehouseMaterialVO> wmVoList, String materialCode, String refType, String refId, SaleOrder saleOrder, Long userId, Connection connection) throws Exception {
        for (WarehouseMaterialVO wmVo : wmVoList) {
            if (wmVo.getLockedNumber() == null || wmVo.getLockedNumber().doubleValue() <= 0) continue;

            // 1. 插入锁定记录
            WarehouseRecord record = new WarehouseRecord();
            record.setType(MESEnum.WAREHOUSE_RECORD_OF_TYPE_LOCK.getValue());
            record.setMaterialCode(materialCode);
            record.setNumber(wmVo.getLockedNumber());
            record.setFromWarehouseId(wmVo.getWarehouseId());
            record.setTenantId(saleOrder.getTenantId());
            record.setRefType(refType);
            record.setRefId(refId);
            record.setSaleOrderId(saleOrder.getId());
            record.setCreateId(userId);
            jdbcClient.jdbcInsert(record, connection);

            // 2. 更新库存表可用数量与锁定数量
            WarehouseMaterial wm = new WarehouseMaterial();
            wm.setWarehouseId(wmVo.getWarehouseId());
            wm.setMaterialCode(materialCode);
            wm = jdbcClient.getJdbcModel(wm, connection);
            if (wm != null) {
                // 可用库存 number 扣减，锁定库存 lockedNumber 增加
                wm.setNumber(wm.getNumber() == null ? null : wm.getNumber().subtract(wmVo.getLockedNumber()));
                wm.setLockedNumber((wm.getLockedNumber() != null ? wm.getLockedNumber() : java.math.BigDecimal.ZERO)
                        .add(wmVo.getLockedNumber()));
                wm.setUpdateId(userId);

                // 根据主键更新（WarehouseMaterial 模型包含 id 字段）
                jdbcClient.jdbcUpdateById(wm, connection);
            }
        }
    }
}
