package com.scpyun.platform.jilinsscgsdp.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.scpyun.base.bean.Page;
import com.scpyun.base.core.annotation.Api;
import com.scpyun.base.core.annotation.ApiOperation;
import com.scpyun.base.core.exception.CustomException;
import com.scpyun.base.db.service.CommonService;
import com.scpyun.platform.jilinsscgsdp.utils.InboundFileListener;
import com.scpyun.platform.jilinsscgsdp.utils.MaterialFileListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

@Api("办公用品入库管理")
@Service("keyDmInbound")
public class KeyDmInboundServiceImpl {
    @Autowired
    private CommonService commonService;

    private static final String SUCCESS = "success";
    private static final int OP_TYPE_RETURN = 1; // 操作类型-归还
    private static final String FIELD_ID = "id";
    private static final String FIELD_INBOUND_ID = "inbound_id";
    private static final String FIELD_DETAILS = "details";
    private static final Logger log = LoggerFactory.getLogger(KeyDmInboundServiceImpl.class);

    private final String namespace = "com.scpyun.platform.standard.jilinsscgsdp.keyDmInbound.";
    private final String detailNamespace = "com.scpyun.platform.standard.jilinsscgsdp.keyDmInboundDetail.";
    private final String materialNamespace = "com.scpyun.platform.standard.jilinsscgsdp.keyDmMaterialLog.";
    private final String borrowNamespace = "com.scpyun.platform.standard.jilinsscgsdp.keyDmBorrow.";
    private final String userNamespace = "com.scpyun.platform.standard.jilinsscgsdp.keyDmUser.";

    private Map<String, String> userMap;

    @PostConstruct
    public void initUserMap() {
        List<Map<String,Object>> userList = commonService.findList(userNamespace + "getUserMap", null);
        // 此时commonService已被注入，非null
        userMap = new HashMap<>();
        for (Map<String, Object> user : userList) {
            String userId = String.valueOf(user.get("Id")); // 转为String，避免类型问题
            String userName = (String) user.get("name");
            if (userId != null && userName != null) { // 空值防御
                userMap.put(userId, userName);
            }
        }
    }

    @ApiOperation(value = "入库记录列表", desc = "分页查询")
    public Page<Map<String, Object>> selectList(Map<String, Object> map) {
        if (map == null) map = new HashMap<>();
        return commonService.findPage(namespace + "selectList", map);
    }

    @ApiOperation(value = "保存入库记录", desc = "新增或修改入库单及明细")
    public String save(Map<String, Object> map) {
        if (map == null) throw new CustomException("参数不能为空");
        Object id = map.get("id");
        List<Map<String, Object>> details = (List<Map<String, Object>>) map.get("details");
        if (id == null || String.valueOf(id).trim().isEmpty()) {
            System.out.println(map);
            map.put("id", UUID.randomUUID().toString());
            map.put("order_no",0);
            // 日期格式转换：兼容 2025-12-26 和 2025-12-25T16:00:00.000Z 两种格式
            String dateStr = (String) map.get("inbound_date");
            Date inbound_date = null;
            if (dateStr != null && !dateStr.trim().isEmpty()) {
                try {
                    if (dateStr.contains("T") || dateStr.contains("Z")) {
                        // 处理带时区的ISO格式：转换为东八区日期
                        DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_INSTANT;
                        Instant instant = Instant.parse(dateStr);
                        // 转换为东八区的LocalDate
                        LocalDate localDate = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
                        inbound_date = java.sql.Date.valueOf(localDate);
                    } else {
                        // 处理纯日期格式（2025-12-26）
                        LocalDate localDate = LocalDate.parse(dateStr);
                        inbound_date = java.sql.Date.valueOf(localDate);
                    }
                } catch (DateTimeParseException e) {
                    throw new CustomException("日期格式错误，支持格式：2025-12-26 或 2025-12-26T16:00:00.000Z");
                }
            }
            map.put("inbound_date", inbound_date);
            map.put("inbound_status",0);
            if (commonService.insert(namespace + "insert", map) != 1) throw new CustomException("保存失败");

            if (details != null) {
                for (Map<String, Object> d : details) {
                    d.put("inbound_id", map.get("id"));
                    d.put("inbound_type", map.get("inbound_type"));
                    d.put("id",UUID.randomUUID().toString());
                    commonService.insert(detailNamespace + "insertDetail", d);
                }
            }
        } else {
            int aff = commonService.update(namespace + "update", map);
            if (aff < 0) throw new CustomException("保存失败");
            commonService.delete(detailNamespace + "deleteDetailsByInboundId", map);
            if (details != null) {
                for (Map<String, Object> d : details) {
                    d.put("id",UUID.randomUUID().toString());
                    d.put("inbound_id", map.get("id"));
                    d.put("inbound_type", map.get("inbound_type"));
                    commonService.insert(detailNamespace + "insertDetail", d);
                }
            }
        }
        return SUCCESS;
    }
    // 删除入库单以及明细
    @Transactional(rollbackFor = Exception.class)
    @ApiOperation(value = "删除入库单", desc = "逻辑删除")
    public String delete(Map<String, Object> map) {
        if (map == null || map.get("id") == null) throw new CustomException("参数缺失");
        int aff = commonService.update(namespace + "delete", map);
        int detail_aff = commonService.update(detailNamespace + "deleteDetailsByInboundId", map);
        if (aff < 0 || detail_aff < 0) throw new CustomException("删除失败");
        return SUCCESS;
    }
    @Transactional(rollbackFor = Exception.class)
    @ApiOperation(value = "执行入库", desc = "将入库单置为已入库，更新库存并写日志")
    public String doInbound(Map<String, Object> map) {
        if (map == null || map.get("id") == null) throw new CustomException("参数缺失");
        // 设置入库状态
        int aff = commonService.update(namespace + "doInbound", map);
        if (aff < 0) throw new CustomException("入库失败");
        Map<String, Object> ret = getById(map);
        ret.put("_user", map.get("_user"));
        // 更新库存明细：调用 mapper 更新库存和日志（mapper 内实现或逐条处理）

        commonService.update(namespace + "updateInventoryByInbound", ret);

        commonService.insert(materialNamespace + "insertLogByInbound", ret);
        return SUCCESS;
    }

    @ApiOperation(value = "待归还列表（来自申领）", desc = "查询待归还记录")
    public Page<Map<String, Object>> selectPendingReturnList(Map<String, Object> map) {
        if (map == null) map = new HashMap<>();
        return commonService.findPage(namespace + "selectPendingReturnList", map);
    }

    /**
     * 处理物资归还
     * 核心逻辑：生成入库单 → 更新申领明细归还数量 → 更新库存 → 记录操作日志
     *
     * @param paramMap 入参Map，必须包含id（申领单ID）
     * @return 处理结果（success/fail）
     * @throws CustomException 业务异常
     */
    @Transactional(rollbackFor = Exception.class)
    @ApiOperation(value = "处理归还", desc = "基于申领生成入库单并更新库存")
    public String processReturn(Map<String, Object> paramMap) {
        // 1. 入参校验（增强版：多层级空值防御）
        validateParam(paramMap);

        try {
            // 2. 获取待归还数据
            Map<String, Object> returnData = getPendingReturnById(paramMap);
            log.info("开始处理物资归还，申领单ID：{}", paramMap.get(FIELD_ID));

            // 3. 生成入库单ID并插入入库主表/明细表
            generateInboundOrder(returnData);

            // 4. 处理明细：更新归还数量、库存
            processReturnDetails(returnData);

            // 5. 记录物资操作日志
            recordMaterialReturnLog(returnData);

            log.info("物资归还处理完成，申领单ID：{}，入库单ID：{}",
                    paramMap.get(FIELD_ID), returnData.get(FIELD_INBOUND_ID));
            return SUCCESS;
        } catch (CustomException e) {
            log.error("处理物资归还业务异常，申领单ID：{}，异常信息：{}",
                    paramMap.get(FIELD_ID), e.getMessage(), e);
            throw e; // 抛出业务异常，触发事务回滚
        } catch (Exception e) {
            log.error("处理物资归还系统异常，申领单ID：{}，异常信息：{}",
                    paramMap.get(FIELD_ID), e.getMessage(), e);
            throw new CustomException("归还处理失败：" + e.getMessage()); // 包装为业务异常
        }
    }

    /**
     * 入参校验：确保核心参数非空
     */
    private void validateParam(Map<String, Object> paramMap) {
        if (paramMap == null || paramMap.get(FIELD_ID) == null) {
            log.error("处理物资归还失败：入参缺失，paramMap={}", paramMap);
            throw new CustomException("参数缺失：申领单ID（id）不能为空");
        }
    }

    /**
     * 生成入库单（主表+明细表）
     */
    private void generateInboundOrder(Map<String, Object> returnData) {
        // 生成唯一入库单ID
        String inboundId = UUID.randomUUID().toString();
        returnData.put(FIELD_INBOUND_ID, inboundId);

        // 插入入库主表
        commonService.insert(namespace + "insertInboundByReturn", returnData);
        log.debug("插入入库主表成功，入库单ID：{}", inboundId);

        // 插入入库明细表
        commonService.insert(namespace + "insertInboundDetailByReturn", returnData);
        log.debug("插入入库明细表成功，入库单ID：{}", inboundId);
    }

    /**
     * 处理归还明细：更新申领明细归还数量、更新库存
     */
    @SuppressWarnings("unchecked")
    private void processReturnDetails(Map<String, Object> returnData) {
        // 安全获取明细列表（避免类型转换异常）
        List<Map<String, Object>> detailList = (List<Map<String, Object>>) returnData.get(FIELD_DETAILS);
        if (detailList == null || detailList.isEmpty()) {
            log.warn("申领单ID：{} 无归还明细数据，跳过库存更新", returnData.get(FIELD_ID));
            return;
        }

        String applicationId = String.valueOf(returnData.get(FIELD_ID));
        for (Map<String, Object> detail : detailList) {
            // 填充申领单ID
            detail.put("application_id", applicationId);
            detail.put("op_type", OP_TYPE_RETURN);
            detail.put("_user", returnData.get("_user"));

            // 更新申领明细归还数量
            commonService.insert(namespace + "updateBorrowDetailReturnedQuantity", detail);
            // 更新库存（借出数量扣减）
            commonService.update(namespace + "updateInventoryBorrowed", detail);

            log.debug("更新库存成功，物资明细：{}", detail);
        }
    }

    /**
     * 记录物资归还操作日志
     */
    private void recordMaterialReturnLog(Map<String, Object> returnData) {
        try {
            returnData.put("inbound_type", 1);
            commonService.insert(materialNamespace + "insertLogByReturn", returnData);
            log.debug("记录物资归还日志成功，申领单ID：{}", returnData.get(FIELD_ID));
        } catch (Exception e) {
            log.error("记录物资归还日志失败，申领单ID：{}", returnData.get(FIELD_ID), e);
            // 日志记录失败是否抛异常？根据业务决策：若日志非核心，可捕获不抛；若核心则抛出
            // throw new CustomException("日志记录失败：" + e.getMessage());
        }
    }

    @ApiOperation(value = "库存查询", desc = "实时库存")
    public Page<Map<String, Object>> selectInventoryList(Map<String, Object> map) {
        if (map == null) map = new HashMap<>();
        return commonService.findPage(namespace + "selectInventoryList", map);
    }

    @ApiOperation(value = "详情", desc = "查询入库/归还详情及日志")
    public Map<String, Object> getById(Map<String, Object> map) {
        if (map == null || map.get("id") == null) throw new CustomException("参数缺失");
        Map<String, Object> ret = new HashMap<>();
        Map<String, Object> rec = commonService.getObject(namespace + "getById", map);
        ret.putAll(rec == null ? new HashMap<>() : rec);
        List<Map<String, Object>> details = commonService.findList(detailNamespace+ "selectDetailsByInboundId", map);
        ret.put("details", details == null ? new java.util.ArrayList<>() : details);
        List<Map<String, Object>> logs = commonService.findList(namespace + "selectLogsByInboundId", map);
        ret.put("logs", logs == null ? new java.util.ArrayList<>() : logs);
        initUserMap();
        ret.put("userName", userMap.get(ret.get("create_by")));
        return ret;
    }
    @ApiOperation(value = "归还详情", desc = "查询入库/归还详情及日志")
    public Map<String, Object> getPendingReturnById(Map<String, Object> map) {
        if (map == null || map.get("id") == null) throw new CustomException("参数缺失");
        Map<String, Object> ret = new HashMap<>();
        Map<String, Object> rec = commonService.getObject(borrowNamespace + "getById", map);
        ret.putAll(rec == null ? new HashMap<>() : rec);
        List<Map<String, Object>> details = commonService.findList(namespace+ "selectPendingReturnDetailsByApplicationId", map);
        ret.put("details", details == null ? new java.util.ArrayList<>() : details);

        return ret;
    }

    @ApiOperation(value = "导入入库单", desc = "解析 Excel 导入入库信息")
    public Map<String, Object> importInbound(Map<String, Object> map, MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        try {
            InboundFileListener listener = new InboundFileListener(commonService, namespace, map);
            EasyExcel.read(file.getInputStream(), com.scpyun.platform.jilinsscgsdp.bean.entity.InboundImp.class, listener).sheet().headRowNumber(1).doRead();
            result = listener.getResult();
        } catch (IOException e) {
            throw new CustomException("导入失败");
        }
        return result;
    }

    @ApiOperation(value = "导入模板下载", desc = "下载入库导入模板")
    public void templateDownload(Map<String, Object> map, HttpServletResponse response) {
        response.setContentType("application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        try {
            String fileName = URLEncoder.encode("入库导入模板", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            response.setHeader("filename", fileName + ".xlsx");
            response.setHeader("Access-Control-Expose-Headers", "filename,Content-Disposition");

            String tpl = "keyInboundImp.xlsx";
            Resource resource = new ClassPathResource(tpl);
            InputStream is = resource.getInputStream();
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is).excelType(ExcelTypeEnum.XLSX).build();
            excelWriter.finish();
        } catch (Exception e) {
            throw new CustomException("模板下载失败");
        }
    }
}


