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.MaterialRequisitionVO;
import easydo.technology.service.FlowNoService;
import easydo.technology.service.MaterialRequisitionService;
import easydo.technology.system.model.SysUser;
import easydo.technology.utils.SecurityUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class MaterialRequisitionServiceImpl implements MaterialRequisitionService {

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

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> getPage(Map<String, Object> map) throws Exception {
        Connection connection = dataSource.getConnection();
        try {
            // 查询领料单分页数据
            Map<String, Object> result = jdbcClient.getJdbcPage(map, MaterialRequisition.class, connection);
            List<MaterialRequisition> list = (List<MaterialRequisition>) result.get("records");
            
            if (list == null || list.isEmpty()) {
                return result;
            }

            // 转换为 VO 对象，并查询明细
            List<MaterialRequisitionVO> voList = new ArrayList<>();
            for (MaterialRequisition model : list) {
                MaterialRequisitionVO vo = new MaterialRequisitionVO();
                
                // 复制主要字段
                vo.setId(model.getId());
                vo.setCode(model.getCode());
                vo.setName(model.getName());
                vo.setDispatchId(model.getDispatchId());
                vo.setOrderId(model.getOrderId());
                vo.setBomId(model.getBomId());
                vo.setRequisitionType(model.getRequisitionType());
                vo.setOutboundStatus(model.getOutboundStatus());
                vo.setStatus(model.getStatus());
                vo.setRequisitionDate(model.getRequisitionDate());
                vo.setReceiverId(model.getReceiverId());
                vo.setRemark(model.getRemark());
                vo.setTenantId(model.getTenantId());
                vo.setCreateId(model.getCreateId());
                vo.setCreateTime(model.getCreateTime());
                vo.setUpdateId(model.getUpdateId());
                vo.setUpdateTime(model.getUpdateTime());
                
                // 查询领料单明细
                MaterialRequisitionItem itemParam = new MaterialRequisitionItem();
                itemParam.setRequisitionId(model.getId());
                List<MaterialRequisitionItem> items = jdbcClient.getJdbcList(itemParam, connection);
                vo.setItems(items);
                
                voList.add(vo);
            }
            
            // 替换 records
            result.put("records", voList);
            return result;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        } finally {
            connection.close();
        }
    }

    @Override
    public MaterialRequisitionVO save(MaterialRequisitionVO model) throws Exception {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        try {
            String tenantId = resolveTenantId(model, connection);

            MaterialRequisition requisition = new MaterialRequisition();
            requisition.setCode(model.getCode());
            requisition.setName(model.getName());
            requisition.setDispatchId(model.getDispatchId());
            requisition.setOrderId(model.getOrderId());
            requisition.setBomId(model.getBomId());
            requisition.setRequisitionType(model.getRequisitionType() != null && !model.getRequisitionType().trim().isEmpty()
                ? model.getRequisitionType()
                : MESEnum.MATERIAL_REQUISITION_TYPE_MANUAL.getValue());
            requisition.setOutboundStatus(model.getOutboundStatus() != null && !model.getOutboundStatus().trim().isEmpty()
                ? model.getOutboundStatus()
                : MESEnum.MATERIAL_REQUISITION_OUTBOUND_STATUS_APPLIED.getValue());
            requisition.setStatus(model.getStatus() != null && !model.getStatus().trim().isEmpty()
                ? model.getStatus()
                : MESEnum.MATERIAL_REQUISITION_OF_STATUS_PENDING.getValue());
            requisition.setRequisitionDate(model.getRequisitionDate());
            requisition.setReceiverId(model.getReceiverId());
            requisition.setRemark(model.getRemark());
            requisition.setTenantId(tenantId);
            requisition.setCreateId(model.getCreateId());
            requisition.setCreateTime(FORMATTER.format(LocalDateTime.now()));

            String requisitionCode = flowNoService.generateMaterialRequisitionCode(requisition, connection);
            requisition.setCode(requisitionCode);
            jdbcClient.jdbcInsert(requisition, connection);

            insertItems(requisition.getId(), tenantId, model.getItems(), connection);

            connection.commit();
            model.setId(requisition.getId());
            model.setCode(requisition.getCode());
            model.setRequisitionType(requisition.getRequisitionType());
            model.setOutboundStatus(requisition.getOutboundStatus());
            model.setStatus(requisition.getStatus());
            model.setTenantId(requisition.getTenantId());
            model.setCreateTime(requisition.getCreateTime());
            return model;
        } catch (Exception e) {
            connection.rollback();
            throw new BizException(e.getMessage());
        } finally {
            connection.close();
        }
    }

    @Override
    public MaterialRequisitionVO update(MaterialRequisitionVO model) throws Exception {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        try {
            if (model.getId() == null || model.getId().trim().isEmpty()) {
                throw new BizException("领料单ID不能为空");
            }

            MaterialRequisition query = new MaterialRequisition();
            query.setId(model.getId());
            MaterialRequisition requisition = jdbcClient.getJdbcModelById(query, connection);
            if (requisition == null) {
                throw new BizException("领料单不存在");
            }

            if (model.getDispatchId() == null || model.getDispatchId().trim().isEmpty()) {
                model.setDispatchId(requisition.getDispatchId());
            }
            if (model.getOrderId() == null || model.getOrderId().trim().isEmpty()) {
                model.setOrderId(requisition.getOrderId());
            }
            if (model.getBomId() == null || model.getBomId().trim().isEmpty()) {
                model.setBomId(requisition.getBomId());
            }
            if (model.getTenantId() == null || model.getTenantId().trim().isEmpty()) {
                model.setTenantId(requisition.getTenantId());
            }

            String tenantId = resolveTenantId(model, connection);

            requisition.setCode(model.getCode());
            requisition.setName(model.getName());
            requisition.setDispatchId(model.getDispatchId());
            requisition.setOrderId(model.getOrderId());
            requisition.setBomId(model.getBomId());
            requisition.setRequisitionType(model.getRequisitionType() != null && !model.getRequisitionType().trim().isEmpty()
                ? model.getRequisitionType()
                : requisition.getRequisitionType());
            requisition.setOutboundStatus(model.getOutboundStatus() != null && !model.getOutboundStatus().trim().isEmpty()
                ? model.getOutboundStatus()
                : requisition.getOutboundStatus());
            requisition.setStatus(model.getStatus() != null && !model.getStatus().trim().isEmpty()
                ? model.getStatus()
                : requisition.getStatus());
            requisition.setRequisitionDate(model.getRequisitionDate());
            requisition.setReceiverId(model.getReceiverId());
            requisition.setRemark(model.getRemark());
            requisition.setTenantId(tenantId);
            requisition.setUpdateId(model.getUpdateId());
            requisition.setUpdateTime(FORMATTER.format(LocalDateTime.now()));
            jdbcClient.jdbcUpdateById(requisition, connection);

            MaterialRequisitionItem removeItemParam = new MaterialRequisitionItem();
            removeItemParam.setRequisitionId(requisition.getId());
            jdbcClient.jdbcRemove(removeItemParam, connection);
            insertItems(requisition.getId(), tenantId, model.getItems(), connection);

            connection.commit();
            model.setCode(requisition.getCode());
            model.setTenantId(requisition.getTenantId());
            model.setUpdateTime(requisition.getUpdateTime());
            return model;
        } catch (Exception e) {
            connection.rollback();
            throw new BizException(e.getMessage());
        } finally {
            connection.close();
        }
    }

    @Override
    public MaterialRequisitionVO remove(MaterialRequisitionVO model) throws Exception {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        try {
            if (model.getId() == null || model.getId().trim().isEmpty()) {
                throw new BizException("领料单ID不能为空");
            }

            MaterialRequisition requisition = new MaterialRequisition();
            requisition.setId(model.getId());
            MaterialRequisition exist = jdbcClient.getJdbcModelById(requisition, connection);
            if (exist == null) {
                throw new BizException("领料单不存在");
            }

            MaterialRequisitionItem removeItemParam = new MaterialRequisitionItem();
            removeItemParam.setRequisitionId(model.getId());
            jdbcClient.jdbcRemove(removeItemParam, connection);
            jdbcClient.jdbcRemoveById(requisition, connection);

            connection.commit();
            return model;
        } catch (Exception e) {
            connection.rollback();
            throw new BizException(e.getMessage());
        } finally {
            connection.close();
        }
    }

    @Override
    public void generateRequisition(ProductOrderDispatch dispatch, Connection connection) throws Exception {
        try {
            // 1. 检查是否首道工序（order_num 最小的工序）
            // 先查询该工艺路线的所有工序，找出最小的 order_num
            ProcessRouteDetail allRouteQuery = new ProcessRouteDetail();
            allRouteQuery.setRouteId(dispatch.getRouteId());
            List<ProcessRouteDetail> allRouteDetails = jdbcClient.getJdbcList(allRouteQuery, connection);
            
            if (allRouteDetails == null || allRouteDetails.isEmpty()) {
                // 找不到工序信息，不生成领料单
                return;
            }
            
            // 找出最小的 order_num
            Integer minOrderNum = allRouteDetails.stream()
                .map(ProcessRouteDetail::getOrderNum)
                .filter(num -> num != null)
                .min(Integer::compareTo)
                .orElse(null);
            
            if (minOrderNum == null) {
                // 没有有效的 order_num，不生成领料单
                return;
            }
            
            // 查询当前派工的工序信息
            ProcessRouteDetail currentRouteQuery = new ProcessRouteDetail();
            currentRouteQuery.setRouteId(dispatch.getRouteId());
            currentRouteQuery.setStageId(dispatch.getStageId());
            List<ProcessRouteDetail> currentRouteDetails = jdbcClient.getJdbcList(currentRouteQuery, connection);
            
            if (currentRouteDetails == null || currentRouteDetails.isEmpty()) {
                // 找不到当前工序信息，不生成领料单
                return;
            }
            
            ProcessRouteDetail currentRouteDetail = currentRouteDetails.get(0);
            if (currentRouteDetail.getOrderNum() == null || !currentRouteDetail.getOrderNum().equals(minOrderNum)) {
                // 非首道工序，不生成领料单
                return;
            }
            
            // 2. 查询次级 BOM（只查一层，parent_id = bomId）
            ProductBom subBomQuery = new ProductBom();
            subBomQuery.setParentId(dispatch.getBomId());
            subBomQuery.setTenantId(dispatch.getTenantId());
            List<ProductBom> subBoms = jdbcClient.getJdbcList(subBomQuery, connection);
            
            if (subBoms == null || subBoms.isEmpty()) {
                // 无次级 BOM，不生成领料单
                return;
            }
            
            // 3. 创建领料单主表
            MaterialRequisition requisition = new MaterialRequisition();
            requisition.setDispatchId(dispatch.getId());
            requisition.setOrderId(dispatch.getOrderId());
            requisition.setBomId(dispatch.getBomId());
            requisition.setRequisitionType(MESEnum.MATERIAL_REQUISITION_TYPE_AUTO.getValue());  // 自动生成
            requisition.setOutboundStatus(MESEnum.MATERIAL_REQUISITION_OUTBOUND_STATUS_APPLIED.getValue());  // 申请完毕
            requisition.setStatus(MESEnum.MATERIAL_REQUISITION_OF_STATUS_PENDING.getValue());
            // requisition_date 领料日期，在实际领料时填入，生成时不填
            requisition.setTenantId(dispatch.getTenantId());
            requisition.setCreateId(dispatch.getCreateId());
            requisition.setCreateTime(FORMATTER.format(LocalDateTime.now()));
            
            // 生成领料单编号
            String requisitionCode = flowNoService.generateMaterialRequisitionCode(requisition, connection);
            requisition.setCode(requisitionCode);
            requisition.setName(requisitionCode);
            
            jdbcClient.jdbcInsert(requisition, connection);
            
            // 4. 创建领料单明细（每个次级 BOM 一条明细）
            for (ProductBom subBom : subBoms) {
                MaterialRequisitionItem item = new MaterialRequisitionItem();
                item.setRequisitionId(requisition.getId());
                item.setMaterialCode(subBom.getMaterialCode());
                item.setMaterialName(subBom.getMaterialName());
                
                // 计算需求数量：派工数量 × 次级BOM用量
                BigDecimal requiredQuantity = new BigDecimal(dispatch.getOrderNum())
                    .multiply(subBom.getQuantity() != null ? subBom.getQuantity() : BigDecimal.ZERO);
                item.setRequiredQuantity(requiredQuantity);
                
                item.setActualQuantity(BigDecimal.ZERO);
                
                // 查询物料信息获取单位
                String unit = null;
                String specification = null;
                if (subBom.getMaterialCode() != null) {
                    ProductMaterial materialQuery = new ProductMaterial();
                    materialQuery.setCode(subBom.getMaterialCode());
                    materialQuery.setTenantId(dispatch.getTenantId());
                    List<ProductMaterial> materials = jdbcClient.getJdbcList(materialQuery, connection);
                    if (materials != null && !materials.isEmpty()) {
                        unit = materials.get(0).getUnit();
                        specification = materials.get(0).getSpecification();
                    }
                }
                item.setUnit(unit);
                item.setSpecification(specification);
                
                item.setCreateTime(FORMATTER.format(LocalDateTime.now()));
                
                jdbcClient.jdbcInsert(item, connection);
            }
            
        } catch (Exception e) {
            throw new BizException("生成领料单失败: " + e.getMessage());
        }
    }

    @Override
    public void updateStatus(String requisitionId, String status, Connection connection) throws Exception {
        try {
            MaterialRequisition requisition = new MaterialRequisition();
            requisition.setId(requisitionId);
            requisition = jdbcClient.getJdbcModelById(requisition, connection);
            
            if (requisition == null) {
                throw new BizException("领料单不存在");
            }
            
            Long userId = SecurityUtils.getCurrentUserId();
            requisition.setStatus(status);
            requisition.setUpdateId(userId);
            requisition.setUpdateTime(FORMATTER.format(LocalDateTime.now()));
            
            jdbcClient.jdbcUpdateById(requisition, connection);
            
        } catch (Exception e) {
            throw new BizException("更新领料单状态失败: " + e.getMessage());
        }
    }

    private String resolveTenantId(MaterialRequisitionVO model, Connection connection) throws Exception {
        if (model.getDispatchId() != null && !model.getDispatchId().trim().isEmpty()) {
            ProductOrderDispatch dispatchQuery = new ProductOrderDispatch();
            dispatchQuery.setId(model.getDispatchId());
            ProductOrderDispatch dispatch = jdbcClient.getJdbcModelById(dispatchQuery, connection);
            if (dispatch == null) {
                throw new BizException("关联派工单不存在");
            }

            if (model.getOrderId() == null || model.getOrderId().trim().isEmpty()) {
                model.setOrderId(dispatch.getOrderId());
            } else if (!model.getOrderId().equals(dispatch.getOrderId())) {
                throw new BizException("领料单工单与派工单不匹配");
            }

            if (model.getBomId() == null || model.getBomId().trim().isEmpty()) {
                model.setBomId(dispatch.getBomId());
            } else if (!model.getBomId().equals(dispatch.getBomId())) {
                throw new BizException("领料单BOM与派工单不匹配");
            }

            if (dispatch.getTenantId() != null && !dispatch.getTenantId().trim().isEmpty()) {
                model.setTenantId(dispatch.getTenantId());
                return dispatch.getTenantId();
            }
        }

        boolean hasOrderId = model.getOrderId() != null && !model.getOrderId().trim().isEmpty();
        boolean hasBomId = model.getBomId() != null && !model.getBomId().trim().isEmpty();
        if (hasOrderId || hasBomId) {
            if (!hasOrderId || !hasBomId) {
                throw new BizException("工单ID和BOM ID需要同时填写");
            }

            ProductOrder orderQuery = new ProductOrder();
            orderQuery.setId(model.getOrderId());
            ProductOrder order = jdbcClient.getJdbcModelById(orderQuery, connection);
            if (order == null) {
                throw new BizException("工单不存在");
            }

            ProductOrderBom bomQuery = new ProductOrderBom();
            bomQuery.setOrderId(model.getOrderId());
            bomQuery.setBomId(model.getBomId());
            List<ProductOrderBom> orderBomList = jdbcClient.getJdbcList(bomQuery, connection);
            if (orderBomList == null || orderBomList.isEmpty()) {
                throw new BizException("工单中不存在该BOM");
            }

            model.setTenantId(order.getTenantId());
            return order.getTenantId();
        }

        if (model.getTenantId() != null && !model.getTenantId().trim().isEmpty()) {
            return model.getTenantId();
        }

        Long currentUserId = SecurityUtils.getCurrentUserId();
        SysUser userQuery = new SysUser();
        userQuery.setId(currentUserId);
        SysUser user = jdbcClient.getJdbcModelById(userQuery, connection);
        if (user == null || user.getTenantId() == null || user.getTenantId().trim().isEmpty()) {
            throw new BizException("无法获取当前用户租户信息");
        }
        model.setTenantId(user.getTenantId());
        return user.getTenantId();
    }

    private void insertItems(String requisitionId, String tenantId, List<MaterialRequisitionItem> items, Connection connection) throws Exception {
        if (items == null || items.isEmpty()) {
            return;
        }

        for (MaterialRequisitionItem item : items) {
            item.setId(null);
            item.setRequisitionId(requisitionId);
            if (item.getActualQuantity() == null) {
                item.setActualQuantity(BigDecimal.ZERO);
            }
            item.setCreateTime(FORMATTER.format(LocalDateTime.now()));
            jdbcClient.jdbcInsert(item, connection);
        }
    }
}
