package com.fizz.business.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.PlanStatusEnum;
import com.fizz.business.constants.enums.WebOperateMatEnum;
import com.fizz.business.dto.CoilHeadDTO;
import com.fizz.business.dto.CoilPositionDTO;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.form.AdjustPosForm;
import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.form.WebOperateMatForm;
import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.ProMatmapService;
import com.fizz.business.service.TrackService;
import com.fizz.business.service.client.RedisCacheManager;
import com.fizz.business.utils.CalcUtil;
import com.fizz.business.utils.CoilMeasUtil;
import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil;
import com.fizz.business.vo.CrmPdiPlanVO;
import com.fizz.business.vo.ReturnInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.fizz.business.constants.enums.WebOperateMatEnum.*;


/**
 * @author chenhao
 * @date 2023/05/18
 */
@Slf4j
@Service
public class TrackServiceImpl implements TrackService {

    @Autowired
    RedisCacheManager redisCacheManager;

    @Autowired
    ProMatmapService proMatmapService;

    @Autowired
    CrmPdiPlanService crmPdiPlanService;

    @Override
    public void webOperateMat(WebOperateMatForm form) {

        log.info("web operate mat: {}", JSON.toJSONString(form));
        form.getOperation().operate(form);
        proMatmapService.flushMatmap();
    }

    @Override
    public void l1OperateMat(L1OperateMatForm form) {

        log.info("l1 operate mat: {}", JSON.toJSONString(form));
        form.getOperation().operate(form);
        proMatmapService.flushMatmap();
    }

    @Override
    public ReturnInfoVO getReturnInfo(int posIdx) {
        MatmapDTO matmap = MatmapUtil.getMatmap(posIdx);
        if (MatmapUtil.notReady(matmap)) {
            log.warn("Matmap not ready for position index: {}", posIdx);
            return ReturnInfoVO.builder()
                    .returnWeight(0.0)
                    .returnType(null)
                    .entryMatId(null)
                    .planId(null)
                    .build();
        }

        CrmPdiPlanVO planVO = crmPdiPlanService.getByCoilIdAndOperId(matmap.getPlanId());
        if (planVO == null) {
            log.warn("Plan not found for planId: {}", matmap.getPlanId());
            return ReturnInfoVO.builder()
                    .returnWeight(0.0)
                    .returnType(null)
                    .entryMatId(matmap.getMatId())
                    .planId(Long.valueOf(matmap.getPlanId()))
                    .build();
        }
        
        String returnType = null;
        double returnWt = 0;
        if (Objects.equals(planVO.getStatus(), PlanStatusEnum.PRODUCING.name())) {
            double coiledLength = redisCacheManager.getStripLocation();
            double calcCoilWeight = CalcUtil.calcCoilWeight(coiledLength, planVO.getEntryThick(), planVO.getEntryWidth());
            if (planVO.getEntryWeight() != null && calcCoilWeight > 0) {
                returnWt = planVO.getEntryWeight().divide(BigDecimal.valueOf(calcCoilWeight)).setScale(2, RoundingMode.HALF_UP).doubleValue();
            } else {
                log.warn("Invalid entry weight or calc coil weight: entryWeight={}, calcCoilWeight={}", planVO.getEntryWeight(), calcCoilWeight);
                returnWt = 0.0;
            }
            returnType = HALF_RETURN.name();
        } else if (Objects.equals(planVO.getStatus(), PlanStatusEnum.ONLINE.name())) {
            if (planVO.getEntryWeight() != null) {
                returnWt = planVO.getEntryWeight().doubleValue();
            } else {
                log.warn("Entry weight is null for planId: {}", planVO.getId());
                returnWt = 0.0;
            }
            returnType = ALL_RETURN.name();
        } else {
            log.error("invalid plan status[{}], planId={}", planVO.getStatus(), planVO.getId());
        }

        return ReturnInfoVO.builder()
                .returnWeight(NumberUtil.round(returnWt, 3).doubleValue())
                .returnType(returnType)
                .entryMatId(planVO.getCoilid())
                .planId(planVO.getId())
                .build();
    }

    /**
     * 调整钢卷位置
     * 更新matmap
     * 更新焊缝位置
     *
     * @param form
     */
    @Override
    public void adjustPosition(AdjustPosForm form) {
        DeviceEnum targetDevice = DeviceEnum.fromName(form.getTargetPos().name());
        Assert.notNull(targetDevice, "目标位置设备不存在");

        DeviceEnum currentDevice = DeviceEnum.fromName(form.getCurrentPos().name());
        Assert.notNull(currentDevice, "当前位置设备不存在");

        // 更新matmap
        refreshMatmap(targetDevice, currentDevice, form);
        WebSocketUtil.sendMatmapMsg();
    }

    private boolean pass(Integer startPos, Integer endPos, Integer currPos) {
        // 跳过2#开卷机
        if (Objects.equals(startPos, DeviceEnum.POR1.getIdx())
                && Objects.equals(currPos, DeviceEnum.POR2.getIdx())) {
            return true;
        }
        // 不修改开卷机的卷号
        if (CoilMeasUtil.getPorIdxList().contains(endPos)) {
            return true;
        }
        // 多个卷取机需要跳过1#卷取机
        if (Objects.equals(endPos, DeviceEnum.TR.getIdx())
               ) {
            return true;
        }
        return false;
    }

    private void refreshMatmap(DeviceEnum targetDevice, DeviceEnum currDevice, AdjustPosForm form) {
        Integer targetPosIdx = targetDevice.getIdx();
        Integer currPosIdx = currDevice.getIdx();
        MatmapDTO target = MatmapUtil.getMatmap(targetPosIdx);
        MatmapDTO curr = MatmapUtil.getMatmap(currPosIdx);
        
        if (curr == null || target == null) {
            log.warn("Matmap is null - curr: {}, target: {}, currPosIdx: {}, targetPosIdx: {}", 
                    curr, target, currPosIdx, targetPosIdx);
            return;
        }
        
        if (Objects.equals(curr.getMatId(), target.getMatId())) {
            return;
        }

        if (targetPosIdx < currPosIdx) {
            // 目标位置小于当前位置，回退
            // 设置目标位置->当前位置的matmap
            for (int idx = targetPosIdx; idx <= currPosIdx; idx++) {
                if (pass(targetPosIdx, currPosIdx, idx)) continue;
                MatmapUtil.setMatmap(idx, curr.getMatId(), curr.getPlanId(), curr.getPlanNo());
            }

            // 从卷取机回退需要移除prevHead，并创建currHead
//            if (CoilMeasUtil.getTrIdxList().contains(currPosIdx)) {
//                CoilHeadDTO head = new CoilHeadDTO();
//                head.setMatId(form.getMatId());
//                head.setPlanNo(form.getPlanNo());
//                head.setPlanId(form.getPlanId());
//                head.setPosIdx(targetPosIdx);
//                redisCacheManager.removePrevAndMergeHead();
//                // 设置当前带头长度为焊缝距离
//                head.setPos(redisCacheManager.getStripLocation());
//                redisCacheManager.addHead(head);
//                log.info("add curr head: {}", JSON.toJSONString(head));
//            }
        } else if (targetPosIdx > currPosIdx) {
            // 目标位置大于当前位置，前进
            // 设置当前位置->目标位置的matmap
            for (int idx = currPosIdx; idx <= targetPosIdx; idx++) {
                if (pass(currPosIdx, targetPosIdx, idx)) continue;
                MatmapUtil.setMatmap(idx, curr.getMatId(), curr.getPlanId(), curr.getPlanNo());
            }

            // 从开卷机前进
            // 移动到卷取机-->创建prevHead
            // 移动到中间-->创建currHead
            // 计划状态变更为PRODUCING
            if (CoilMeasUtil.getPorIdxList().contains(currPosIdx)) {
                CoilHeadDTO head = new CoilHeadDTO();
                head.setMatId(curr.getMatId());
                head.setPlanNo(curr.getPlanNo());
                head.setPlanId(curr.getPlanId());
                head.setPosIdx(targetPosIdx);
                head.setPorIdx(currPosIdx);
                // 移动到卷取机需要创建prevHead
                if (CoilMeasUtil.getTrIdxList().contains(targetPosIdx)) {
                    head.setPos(888);
                    redisCacheManager.setPrevHead(head);
                }
                // 移动到中间时需要创建currHead
                else {
                    // 设置当前带头长度为焊缝距离
                    head.setPos(redisCacheManager.getStripLocation());
                    redisCacheManager.removeHead(target.getMatId());
                    redisCacheManager.addHead(head);
                }

                // 判断当前计划状态，如果是ONLINE，需要变更为PRODUCING
                CrmPdiPlanVO plan = crmPdiPlanService.getByCoilIdAndOperId(curr.getMatId());
                if (Objects.equals(plan.getStatus(), ONLINE.name())) {
                    webOperateMat(WebOperateMatForm.builder()
                            .operation(WebOperateMatEnum.PRODUCING)
                            .entryMatId(curr.getMatId())
                            .planId(curr.getPlanId())
                            .planNo(curr.getPlanNo())
                            .build());
                }
            }
        }
    }


    @Override
    public CoilPositionDTO getCoilPosition() {
        CoilPositionDTO position = redisCacheManager.getCoilPosition();
        if (position == null) {
            log.warn("CoilPosition not found in Redis, creating empty position");
            position = new CoilPositionDTO();
        }
        
        List<MatmapDTO> matmapList = redisCacheManager.getMatmapList();
        if (matmapList == null) {
            log.warn("MatmapList not found in Redis, using empty list");
            matmapList = new ArrayList<>();
        }
        
        position.setMatMapList(matmapList);
        return position;
    }


}
