package com.cf.imes.module.plan.service.orderImport;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.exception.ErrorCode;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.id.core.util.SnowflakeIdWorker3rd;
import com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants;
import com.cf.imes.framework.mybatis.core.generator.SnowFlakeGenerator;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
import com.cf.imes.framework.redis.constants.RedisKeyConstants;
import com.cf.imes.framework.redis.util.RedisLockUtil;
import com.cf.imes.module.executor.enums.DataTypeEnum;
import com.cf.imes.module.executor.enums.OrderImportStatusEnum;
import com.cf.imes.module.executor.enums.OrderImportTaskStatusEnum;
import com.cf.imes.module.executor.enums.OrderImportTypeEnum;
import com.cf.imes.module.executor.enums.OrderStatusEnum;
import com.cf.imes.module.plan.controller.admin.orderImport.webcad.vo.WebCadDataReqVO;
import com.cf.imes.module.plan.dal.dataobject.order.OrderDO;
import com.cf.imes.module.plan.dal.dataobject.orderImport.OrderImportTaskDO;
import com.cf.imes.module.plan.dal.mysql.goods.GoodsMapper;
import com.cf.imes.module.plan.dal.mysql.order.OrderMapper;
import com.cf.imes.module.plan.dal.mysql.orderBody.OrderBodyMapper;
import com.cf.imes.module.plan.dal.mysql.orderImport.OrderImportTaskMapper;
import com.cf.imes.module.plan.dal.mysql.plate.PlateMapper;
import com.cf.imes.module.plan.dal.mysql.rawgoods.RawGoodsMapper;
import com.cf.imes.module.plan.service.customplateno.CustomPlateNoGenerateService;
import com.cf.imes.module.plan.service.order.OrderInputProcessor;
import com.cf.imes.module.plan.service.orderImport.factory.WebCadOrderImportFactory;
import com.cf.imes.module.plan.service.orderImport.property.WebCadImportProperties;
import com.cf.imes.module.system.api.systemconfig.SystemConfigApi;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import jakarta.annotation.Resource;

import java.io.File;
import java.io.IOException;
import java.util.zip.GZIPInputStream;

import static com.cf.imes.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORGAN_LOCK_ERROR;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_FAILED;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORGANID_EMPTY_ERROR;
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_NOT_EXISTS_ERROR;

/**
 * 生产单导入服务Service实现类
 *
 * @author Gqr
 * @since 2025/3/4 10:27
 */
@Service
public class WebCadOrderImportServiceImpl implements WebCadOrderImportService {

    private static final Logger log = LoggerFactory.getLogger(WebCadOrderImportServiceImpl.class);
    @Resource
    private OrderMapper orderMapper;

    @Resource
    private SnowFlakeGenerator snowFlakeGenerator;

    @Resource
    private OrderBodyMapper orderBodyMapper;

    @Resource
    private PlateMapper plateMapper;

    @Resource
    private SnowflakeIdWorker3rd idWorker;

    @Resource
    private OrderInputProcessor orderInputProcessor;

    @Resource
    private CustomPlateNoGenerateService customPlateNoGenerateService;

    @Resource
    private RedisLockUtil redisLockUtil;

    @Resource
    private ChenfengCacheProperties chenfengCacheProperties;

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private RawGoodsMapper rawGoodsMapper;

    @Resource
    private DataSourceTransactionManager transactionManager;

    @Resource
    private SystemConfigApi systemConfigApi;

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Resource
    private OrderImportTaskMapper orderImportTaskMapper;

    @Resource
    private RabbitTemplate rabbitTemplate;

    private static final String REDIS_UNIQUEKEY = "webcad";

    @Resource
    private WebCadImportProperties webCadImportProperties;

    @Override
    public boolean webCadOrderImport(MultipartFile file) {
        Long organId = OrganContextHolder.getOrganId();

        if (ObjectUtil.isNull(organId)) {
            throw new ServiceException(WEBCAD_ORDER_IMPORT_ORGANID_EMPTY_ERROR);
        }

        TransactionStatus transactionStatus = transactionManager.getTransaction(TransactionDefinition.withDefaults());
        // 是否同步处理
        boolean sync = true;

        // 异步发送是否成功，没有成功需要把锁解开
        boolean initAsyncSuccess = false;

        Long taskId = null;
        OrderDO orderDO = null;
        JsonFactory factory = new JsonFactory();
        try (GZIPInputStream gzipInputStream = new GZIPInputStream(file.getInputStream());
             JsonParser parser = factory.createParser(gzipInputStream)) {
            factory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);

            int plateCount = 0;
            String fileName = null;
            while (parser.nextToken() != JsonToken.END_OBJECT) {
                String fieldName = parser.getCurrentName();
                if (StringUtils.isEmpty(fieldName)) {
                    continue;
                }

                parser.nextToken(); // 移动到字段值位置

                if ("plateCount".equals(fieldName)) {
                    plateCount = parser.getIntValue();
                } else if ("fileName".equals(fieldName)) {
                    fileName = parser.getText();
                } else if ("OrderNo".equals(fieldName)) {
                    orderDO = analyzeOrder(parser.getText(), organId);
                } else if ("blockData".equals(fieldName)) {
                    if (plateCount < webCadImportProperties.getAsyncPlateThreshold()) {
                        // 上锁
                        addSyncLock(organId);

                        // 在异步阈值范围内，直接解析整个对象
                        ObjectMapper mapper = new ObjectMapper();
                        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                        WebCadDataReqVO webCadDataReqVO = mapper.readValue(parser, WebCadDataReqVO.class);
                        WebCadOrderImportFactory webCadOrderImportFactory = new WebCadOrderImportFactory(organId, orderDO, snowFlakeGenerator, orderMapper, orderBodyMapper, plateMapper,
                                idWorker, orderInputProcessor, goodsMapper, rawGoodsMapper, customPlateNoGenerateService, systemConfigApi, webCadImportProperties.getCadImportPlateNumThreshold(), jdbcTemplate);
                        webCadOrderImportFactory.analyzeTempData(webCadDataReqVO);
                    } else {
                        sync = false;

                        // plateCount 大于阈值时将 blockData 写入文件
                        ObjectMapper mapper = new ObjectMapper();
                        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

                        // 读取结构
                        JsonNode node = mapper.readTree(parser);

                        // 缓存请求，创建导入任务
                        taskId = initiateTask(node, organId, orderDO.getId(), fileName);

                        // 上锁
                        addAsyncLock(taskId);

                        // 发送异步消息
                        sendMessage(taskId);

                        initAsyncSuccess = true;

                        // 跳过当前 blockData 解析步骤
                        parser.skipChildren(); // 忽略 blockData
                    }
                } else {
                    parser.skipChildren(); // 忽略其他字段
                }
            }
            // 事务提交
            transactionManager.commit(transactionStatus);
        } catch (ServiceException se) {
            // 事务回滚
            transactionManager.rollback(transactionStatus);
            // 移除临时文件
            removeCacheFileWhenException(sync, taskId);

            throw se;
        } catch (Exception e) {
            // 事务回滚
            transactionManager.rollback(transactionStatus);
            // 移除临时文件
            removeCacheFileWhenException(sync, taskId);

            ErrorCode webcadOrderImportFailed = WEBCAD_ORDER_IMPORT_FAILED;
            log.error(webcadOrderImportFailed.getMsg(), e);
            throw new ServiceException(webcadOrderImportFailed);
        } finally {
            unlock(sync, initAsyncSuccess, organId, taskId);
        }
        return sync;
    }

    /**
     * 解析生产单信息
     *
     * @param orderNo
     */
    private OrderDO analyzeOrder(String orderNo, Long organId) {
        // 查询生产单信息
        OrderDO orderDOFromOrderNo = orderMapper.selectOne(new LambdaUpdateWrapper<OrderDO>()
                .eq(OrderDO::getId, NumberUtil.parseLong(orderNo))
                .eq(OrderDO::getOrganId, organId)
                .eq(OrderDO::getDeleted, false));
        if (ObjectUtil.isNull(orderDOFromOrderNo)) {
            throw ServiceExceptionUtil.exception(WEBCAD_ORDER_NOT_EXISTS_ERROR, orderNo);
        } else {
            Integer status = orderDOFromOrderNo.getStatus();
            // 更新生产单来源为webcad
            LambdaUpdateWrapper<OrderDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<OrderDO>()
                    .eq(OrderDO::getId, orderDOFromOrderNo.getId())
                    .eq(OrderDO::getOrganId, organId)
                    .set(OrderDO::getDataType, DataTypeEnum.API_IMPORT.getStatus());
            // 如果导入生产单为空单，则更新为新单
            if (OrderStatusEnum.EMPTY.getStatus().equals(status)) {
                lambdaUpdateWrapper.set(OrderDO::getStatus, OrderStatusEnum.NEW_ORDER.getStatus());
            }
            orderMapper.update(lambdaUpdateWrapper);
        }

        return orderDOFromOrderNo;
    }

    /**
     * 缓存请求，发起异步
     *
     * @param node
     * @param organId
     * @return
     * @throws IOException
     */
    private Long initiateTask(JsonNode node, Long organId, Long orderId, String fileName) throws IOException {
        // 创建任务
        Long taskId = (Long) snowFlakeGenerator.nextId(null);
        // 创建导入任务
        OrderImportTaskDO orderImportTaskDO = OrderImportTaskDO.builder()
                .id(taskId)
                .type(OrderImportTypeEnum.API.getType())
                .importStatus(OrderImportStatusEnum.IMPORT_NOT_YET.getStatus())
                .status(OrderImportTaskStatusEnum.CREATE_NOT_CONSUME.getStatus())
                .fileName(fileName)
                .orderId(orderId)
                .organId(organId)
                .build();
        orderImportTaskMapper.insert(orderImportTaskDO);
        // 缓存请求到本地磁盘
        ObjectMapper objectMapper = new ObjectMapper();
        File file = new File(webCadImportProperties.getTempFilePath() + File.separator + taskId + ".json");
        objectMapper.writeValue(file, node);

        return taskId;
    }

    /**
     * 发送异步消息
     *
     * @param taskId
     */
    private void sendMessage(Long taskId) {
        Message message = MessageBuilder
                .withBody(taskId.toString().getBytes())
                .setHeader("peek", DynamicDataSourceContextHolder.peek())
                .setExpiration("60000")  // 设置消息级 TTL（60 秒）
                .build();
        //如果处于事务中，待事务提交成功了再执行后续操作，防止事务未提交完成导致消费端查不到数据
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 数据库操作全部完成发送消息
                    rabbitTemplate.convertAndSend(RabbitMqConstants.ORDER_IMPORT_WEBCAD_EXCHANGE, webCadImportProperties.getNode(), message);
                }
            });
        } else {
            // 数据库操作全部完成发送消息
            rabbitTemplate.convertAndSend(RabbitMqConstants.ORDER_IMPORT_WEBCAD_EXCHANGE, webCadImportProperties.getNode(), message);
        }
    }

    /**
     * 加同步导入锁
     *
     * @param organId
     */
    private void addSyncLock(Long organId) {
        String importLockKey = String.format(RedisKeyConstants.ORDER_IMPORT_LOCK_KEY, organId);
        // 检查机构导入锁
        boolean lockResult = redisLockUtil.acquireLock(importLockKey, REDIS_UNIQUEKEY, chenfengCacheProperties.getLockTimeout());
        if (!lockResult) {
            throw new ServiceException(ORDER_IMPORT_ORGAN_LOCK_ERROR);
        }
    }

    /**
     * 加异步导入锁
     *
     * @param taskId
     */
    private void addAsyncLock(Long taskId) {
        String importLockKey = String.format(RedisKeyConstants.ORDER_IMPORT_LOCK_KEY, getUserOrganId());
        // 检查机构导入锁
        boolean lockResult = redisLockUtil.acquireLock(importLockKey, String.valueOf(taskId), chenfengCacheProperties.getLockTimeout());
        if (!lockResult) {
            throw new ServiceException(ORDER_IMPORT_ORGAN_LOCK_ERROR);
        }
    }

    /**
     * 解锁
     */
    private void unlock(boolean sync, boolean initAsyncSuccess, Long organId, Long taskId) {
        if(sync) {
            // 解同步锁
            redisLockUtil.releaseLock(String.format(RedisKeyConstants.ORDER_IMPORT_LOCK_KEY, organId), REDIS_UNIQUEKEY);
        } else {
            // 异步发送消息失败了，解开异步锁
            if (!initAsyncSuccess && ObjectUtil.isNotNull(taskId)) {
                redisLockUtil.releaseLock(String.format(RedisKeyConstants.ORDER_IMPORT_LOCK_KEY, organId), String.valueOf(taskId));
            }
        }
    }


    /**
     * 异常时移除临时文件
     */
    private void removeCacheFileWhenException(boolean sync, Long taskId) {
        if (!sync && ObjectUtil.isNotNull(taskId)) {
            FileUtil.del(webCadImportProperties.getTempFilePath() + File.separator + taskId + ".json");
        }
    }
}
