package com.ruoyi.web.controller.inventory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.UUID;
import javax.servlet.http.HttpServletResponse;

import com.ruoyi.inventory.domain.TO.InboundItemsTO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundFinishTemplateVO;
import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTRDCTemplateVO;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import com.ruoyi.inventory.service.impl.InboundOrdersServiceImpl;
import com.ruoyi.system.service.ISysDictDataService;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.inventory.domain.InboundOrders;
import com.ruoyi.inventory.service.IInboundOrdersService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;

/**
 * 入库单主Controller
 * 
 * @author ruoyi
 * @date 2025-12-02
 */
@RestController
@RequestMapping("/inventory/inbound")
public class InboundOrdersController extends BaseController
{
    @Autowired
    private IInboundOrdersService inboundOrdersService;
    @Autowired
    private ISysDictDataService iSysDictDataService;
    private static final Logger log = LoggerFactory.getLogger(InboundOrdersController.class);
    /**
     * 查询入库单主列表
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:list')")
    @GetMapping("/list")
    public TableDataInfo list(InboundOrders inboundOrders)
    {
        startPage();
        List<InboundOrders> list = inboundOrdersService.selectInboundOrdersList(inboundOrders);
        return getDataTable(list);
    }

    /**
     * 获取字典映射表
     */
    private Map<Object, Object> getDictMap(String dictType) {
        List<SysDictData> dictList = iSysDictDataService.selectDictDataByType(dictType);
        Map<Object, Object> dictMap = new HashMap<>();
        for (SysDictData dictData : dictList) {
            dictMap.put(dictData.getDictValue(), dictData.getDictLabel());
        }
        return dictMap;
    }
    /**
     * 导出入库单主列表
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:export')")
    @Log(title = "入库单主", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, InboundOrders inboundOrders) {
        // 1. 基础空值校验：查询结果为空时直接导出空表格，避免后续循环NPE
        List<InboundItemsTO> list = inboundOrdersService.selectInboundOrdersAndItems(inboundOrders);
        if (list == null || list.isEmpty()) {
            ExcelUtil<InboundItemsTO> util = new ExcelUtil<>(InboundItemsTO.class);
            // 替换 List.of() 为 Java 8 兼容的空列表创建方式
            util.exportExcel(response, new ArrayList<>(), "入库单导出数据");
            return;
        }

        // 2. 字典映射表空值校验：避免字典表为空导致的NPE
        Map<Object, Object> colorDictMap = getDictMap("label_color");
        colorDictMap = MapUtils.emptyIfNull(colorDictMap); // 空Map转为空集合，避免后续get操作NPE

        // 3. 循环处理每条数据，增加全字段空值保护
        for (InboundItemsTO inboundItem : list) {
            // 跳过空对象
            if (inboundItem == null) {
                continue;
            }

            // ========== 3.1 数学运算安全处理（避免除零/空值运算NPE） ==========
            // 初始化默认值，避免空值运算
            Double weight = inboundItem.getWeight() == null ? 0.0 : inboundItem.getWeight();
            Double actualQuantity = inboundItem.getActualQuantity() == null ? 0.0 : inboundItem.getActualQuantity();
            Double actualPackages = inboundItem.getActualPackages() == null ? 1.0 : inboundItem.getActualPackages();

            // 安全除法：避免除零异常
            double quantityPerPackage = actualPackages == 0 ? 0 : actualQuantity / actualPackages;
            // 计算重量（空值保护）
            double packageWeight = weight * quantityPerPackage;

            // 赋值（避免setter传入null）
            inboundItem.setPackageWeight(packageWeight);
            inboundItem.setUnitWeight(packageWeight); // 与packageWeight值相同

            // ========== 3.2 标签颜色名称映射（空值保护） ==========
            String labelColor = inboundItem.getLabelColor() == null ? "" : inboundItem.getLabelColor().toString();
            String labelColorName = (String) colorDictMap.get(labelColor);
            inboundItem.setLabelColorName(StringUtils.defaultIfBlank(labelColorName, "")); // 空值转为空字符串

            // ========== 3.3 订单类型分支处理（空值保护） ==========
            String orderTypeId = StringUtils.defaultIfBlank(inboundItem.getOrderTypeId(), "-");
            String remark = StringUtils.defaultIfBlank(inboundItem.getRemark(), "-");
            String systemNo = StringUtils.defaultIfBlank(inboundItem.getSystemNo(), "-");
            String orderId = StringUtils.defaultIfBlank(inboundItem.getOrderId(), "-");

            switch (orderTypeId) {
                case "1":
                    inboundItem.setRelocation(remark);
                    break;
                case "2":
                    inboundItem.setFinishedLocation(remark);
                    break;
                case "3":
                    inboundItem.setRemarkTrdc(remark);
                    inboundItem.setFinishedSystemNo(systemNo);
                    inboundItem.setFinishedOrderId(orderId);
                    break;
                // 增加默认分支，避免未匹配的情况
                default:
                    // 可选：记录日志或设置默认值
                    break;
            }
        }

        // 4. 导出Excel（最终兜底：确保传入非null列表）
        ExcelUtil<InboundItemsTO> util = new ExcelUtil<>(InboundItemsTO.class);
        util.exportExcel(response, list, "入库单导出数据");
    }

    /**
     * 获取入库单主详细信息
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") String id)
    {
        return success(inboundOrdersService.selectInboundOrdersById(id));
    }

    /**
     * 新增入库单主
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:add')")
    @Log(title = "入库单主", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody InboundOrders inboundOrders)
    {
        inboundOrders.setId(UUID.randomUUID().toString());
        return toAjax(inboundOrdersService.insertInboundOrders(inboundOrders));
    }

    /**
     * 修改入库单
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:edit')")
    @Log(title = "入库单", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody InboundOrders inboundOrders)
    {
        return toAjax(inboundOrdersService.updateInboundOrders(inboundOrders));
    }

    /**
     * 删除入库单
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:remove')")
    @Log(title = "入库单", businessType = BusinessType.DELETE)
	@DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable String[] ids)
    {
        return toAjax(inboundOrdersService.deleteInboundOrdersByIds(ids));
    }

    /**
     * 下载入库单导入模板
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:importTemplate')")
    @Log(title = "入库导入模板", businessType = BusinessType.IMPORT)
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response,@RequestParam("orderType") Integer orderType)
    {
        switch(orderType){
            case 1:
                ExcelUtil<InboundTemplateVO> util = new ExcelUtil<InboundTemplateVO>(InboundTemplateVO.class);
                util.importTemplateExcel(response, "入库单及入库物料明细信息");
                break;
            case 2:
                ExcelUtil<InboundFinishTemplateVO> util1 = new ExcelUtil<InboundFinishTemplateVO>(InboundFinishTemplateVO.class);
                util1.importTemplateExcel(response, "成品入库单及入库物料明细信息");
                break;
            case 3:
                ExcelUtil<InboundTRDCTemplateVO> util2 = new ExcelUtil<InboundTRDCTemplateVO>(InboundTRDCTemplateVO.class);
                util2.importTemplateExcel(response, "TRDC退库入库单及入库物料明细信息");
                break;
        }
    }

    /**
     * 导入入库单物料明细
     */
    @PreAuthorize("@ss.hasPermi('inventory:inbound:import')")
    @Log(title = "入库信息导入", businessType = BusinessType.IMPORT)
    @PostMapping("/import")
    public AjaxResult importTemplate(@RequestParam("file") MultipartFile file,
                                     @RequestParam("updateSupport") Integer updateSupport,
                                     @RequestParam(value = "orderType", required = false) Integer orderType) throws Exception {
        // 防护1：校验文件非空
        if (file == null || file.isEmpty()) {
            return error("导入文件不能为空！");
        }
        // 防护2：校验文件格式
        String fileName = file.getOriginalFilename();
        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
            return error("仅支持Excel格式文件（.xlsx/.xls）！");
        }

        // 防护3：校验orderType非空且合法
        if (orderType == null || !Arrays.asList(1, 2, 3).contains(orderType)) {
            return error("导入类型不能为空，当前仅支持1/2/3！");
        }

        // 2. 解析Excel表头（适配EasyExcel 2.x，无interrupt、无readRowNumber）
        List<String> headerList = new ArrayList<>();
        // 标记：是否已解析表头（避免重复处理）
        AtomicBoolean headerParsed = new AtomicBoolean(false);
        try {
            EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Object>() {
                        // 解析表头（2.x 中invokeHeadMap只会执行一次）
                        @Override
                        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
                            // 仅首次执行时解析表头
                            if (!headerParsed.get()) {
                                for (String header : headMap.values()) {
                                    headerList.add(header.trim()); // 去空格存入
                                }
                                headerParsed.set(true); // 标记表头已解析完成
                                log.info("Excel表头解析完成，表头列表：{}", headerList);
                            }
                        }

                        // 解析数据行（表头解析完成后，直接返回不处理）
                        @Override
                        public void invoke(Object data, AnalysisContext context) {
                            // 逻辑终止：表头解析完后，数据行直接跳过
                            return;
                        }

                        // 解析完成（空实现即可）
                        @Override
                        public void doAfterAllAnalysed(AnalysisContext context) {}
                    })
                    .sheet() // 读取第一个sheet
                    .headRowNumber(1) // 指定表头在第1行（2.x 核心配置）
                    .doRead(); // 执行解析

        } catch (IOException e) {
            log.error("解析Excel表头失败", e);
            return AjaxResult.error("解析Excel文件失败：" + e.getMessage());
        }
        // 防护4：表头解析为空的情况
        if (CollectionUtils.isEmpty(headerList)) {
            return error("未解析到Excel表头，请检查模板是否有表头行！");
        }

        // 3. 通用导入逻辑（抽取重复代码，避免冗余）
        String message = handleImport(InboundTemplateVO.class, file, headerList, updateSupport, getUsername(), orderType);
        return success(message);
    }

    /**
     * 根据orderType获取对应的VO类
     */
    private Class<?> getVOClassByOrderType(Integer orderType) {
        switch (orderType) {
            case 1: return InboundTemplateVO.class;
            case 2: return InboundFinishTemplateVO.class;
            case 3: return InboundTRDCTemplateVO.class;
            default: throw new ServiceException("不支持的导入类型,请联系管理员" + orderType);
        }
    }
    private static final Map<Class<?>, List<String>> REQUIRED_HEADS_CACHE = new ConcurrentHashMap<>();

    /**
     * 通用导入逻辑（泛型适配不同VO）
     */
    private <T> String handleImport(Class<T> clazz, MultipartFile file, List<String> headerList,
                                    Integer updateSupport, String operName, Integer orderType) throws Exception {

        // 1. 获取必填表头
        List<String> requiredExcelHeads = REQUIRED_HEADS_CACHE.computeIfAbsent(clazz, cls -> {
            List<String> heads = new ArrayList<>();
            for (Field field : cls.getDeclaredFields()) {
                if (field.isAnnotationPresent(Excel.class)) {
                    heads.add(cleanHeader(field.getAnnotation(Excel.class).name()));
                }
            }
            return Collections.unmodifiableList(heads);
        });

        // 2. 安全清洗表头
        final List<String> finalHeaderList =
                (headerList != null ? headerList : Collections.<String>emptyList()).stream()
                        .map(this::cleanHeader)
                        .collect(Collectors.toList());

        // 3. 校验表头
        if (!finalHeaderList.containsAll(requiredExcelHeads)) {
            List<String> missingHeads = requiredExcelHeads.stream()
                    .filter(head -> !finalHeaderList.contains(head))
                    .collect(Collectors.toList());
            return "导入数据字段与目标模板不一致，请检查！缺失字段：" + String.join("、", missingHeads);
        }

        // 4. 可选：检查字段顺序（如果需要）
        // checkColumnOrder(requiredExcelHeads, finalHeaderList);

        // 5. 解析Excel
        ExcelUtil<T> util = new ExcelUtil<>(clazz);
        List<T> dataList = util.importExcel(file.getInputStream());
        if (CollectionUtils.isEmpty(dataList)) {
            return "Excel中未解析到有效数据，请检查模板是否正确！";
        }

        // 6. 导入数据
        return inboundOrdersService.importInboundOrders(dataList, updateSupport, operName, orderType);
    }
    // 清洗
    private String cleanHeader(String header) {
        if (header == null) return "";
        return header
                .replace("　", " ")      // 全角空格转半角
                .replace("\u00A0", " ")  // 不换行空格（&nbsp;）
                .replace("\u2007", " ")  // 数字空格
                .replace("\u202F", " ")  // 窄空格
                .replaceAll("\\s+", " ") // 合并多个连续空格为单个
                .trim();
    }
    // 辅助方法：获取缺失的列名（备用）
    private String getMissingColumns(List<String> template, List<String> excel) {
        return template.stream()
                .filter(col -> !excel.contains(col))
                .collect(Collectors.joining("、"));
    }

    /**
     * 首页入库次数统计api
     *
     */
    @GetMapping("/inboundCount")
    public AjaxResult inboundCount(){
        int count = inboundOrdersService.countInboundOrders();
        return AjaxResult.success(count);
    }

    /**
     * 按数量统计本月入库物料Top前10
     * @return 结果
     */
    @GetMapping("/countQuantity")
    public AjaxResult countQuantity(){
        List<InboundMaterialTotalVO> list = inboundOrdersService.countInboundMaterialQuantity();
        return AjaxResult.success(list);
    }
    /**
     * 按金额统计本月入库物料Top前10
     * @return 结果
     */
    @GetMapping("/countMoney")
    public AjaxResult countMoney(){
        List<InboundMaterialTotalVO> list = inboundOrdersService.countInboundMaterialMoney();
        return AjaxResult.success(list);
    }
}
