package com.ximai.mes.pro.schedule.strategy;

import com.ximai.mes.pro.schedule.*;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 资源设备选择策略
 */
public class EquipmentSelectionStrategyThXMImpl implements EquipmentSelectionStrategy{

    boolean isMultiple = true;
    private final List<EvaluateEquipment> evaluateEquipmentList = new ArrayList<EvaluateEquipment>(){{
    }};

    @Override
    public EquipmentSelectionResult evaluateOptionalEquipment(Task task) {
        EquipmentSelectionResult rst = new EquipmentSelectionResult();
        List<IOperationTimeCalculator.OperationTimePlan> operationTimePlans = new ArrayList<IOperationTimeCalculator.OperationTimePlan>();
        //必选设备逻辑添加
        List<Equipment> matchEquipments = this.matchEquipment(task, task.getOptionalEquipments());
        List<TaskSchedulingContext.TaskSchedulingResultContext> taskSchResults = new ArrayList<>();
        if(task.getOptionalEquipments().isEmpty()){
            rst.setCause(EquipmentSelectionResult.EquipmentSelectionCause.UN_EQUIPMENT);
            return rst;
        }else if(matchEquipments.isEmpty()){
            rst.setCause(EquipmentSelectionResult.EquipmentSelectionCause.UN_MATCH);
            return rst;
        }
        matchEquipments.forEach(eqt -> {
            TaskSchedulingContext context = new TaskSchedulingContext()
            {{
                setJob(task.getJob());
                setTask(task);
                setEquipment(eqt);
            }};
            IOperationTimeCalculator.OperationTimePlan opertionTimePlan = task.getOperationTimeCalculator().calculate(context);
            taskSchResults.add(new TaskSchedulingContext.TaskSchedulingResultContext(eqt, opertionTimePlan, context));
        });
        taskSchResults.forEach(s->{
            //依次评估设备
            evaluateEquipmentList.forEach(e->{
                double evaluate = e.evaluate(s.getTaskSchedulingContext(), s.getOpertionTimePlan(), taskSchResults);
                rst.getEvaluateDetail().append(e.getClass().getSimpleName()).append(":").append(evaluate).append("*").append(e.weight()).append(",");
                s.getOpertionTimePlan().setScore((evaluate* e.weight())+s.getOpertionTimePlan().getScore());
            });
            rst.getEvaluateDetail().append(s.getEquipment().getWorkUnitName()).append("，合计：").append(s.getOpertionTimePlan().getScore()).append(System.getProperty("line.separator"));
            operationTimePlans.add(s.getOpertionTimePlan());
        });

        if(operationTimePlans.isEmpty()){
            rst.setCause(EquipmentSelectionResult.EquipmentSelectionCause.UN_CALENDER);
            return rst;
        }
        rst.setSelectList(this.evaluateOptionalEquipment(task, operationTimePlans));
        return rst;
    }

    /*
    根据设备评估得分，返回合适的设备产能
    评分第一如直接返回该设备，评分相同返回任意一台设备
     */
    private List<EvaluateOptionalEquipmentResult> evaluateOptionalEquipment(Task task, List<IOperationTimeCalculator.OperationTimePlan> operationTimePlanList) {
        List<EvaluateOptionalEquipmentResult> rst = new ArrayList<EvaluateOptionalEquipmentResult>();
        List<IOperationTimeCalculator.OperationTimePlan> evaludateOpertionTimePlanList = new ArrayList<IOperationTimeCalculator.OperationTimePlan>();
        //按评分倒排
        operationTimePlanList.sort(Comparator.comparing(IOperationTimeCalculator.OperationTimePlan::getScore, Collections.reverseOrder()));
        //评分第一如果数量满足任务，直接返回该数据
        IOperationTimeCalculator.OperationTimePlan firstOperationTimePlan = operationTimePlanList.get(0);
        BigDecimal totalQuantity = firstOperationTimePlan.getPlan().stream().map(s->s.getQuantity()).reduce(BigDecimal.ZERO, BigDecimal::add);
        evaludateOpertionTimePlanList.add(firstOperationTimePlan);
        if(task.getOrderQuantity().compareTo(totalQuantity)>0){
            return rst;
        }
        evaludateOpertionTimePlanList.forEach(operationTimePlan->{
            EvaluateOptionalEquipmentResult evaluateOptionalEquipmentResult = new EvaluateOptionalEquipmentResult()
            {{
                setEquipment(operationTimePlan.getEquipment());
                setDetails(new ArrayList<DetailPlan>());
                setStartedTime(Duration.ofSeconds(Long.MAX_VALUE));
                setEndedTime(Duration.ZERO);
                setScore(operationTimePlan.getScore());
            }};

            for (IOperationTimeCalculator.OperationTimePlanItem item : operationTimePlan.getPlan()) {
                if (evaluateOptionalEquipmentResult.getStartedTime().getSeconds() > item.getStartedTime().getSeconds()) {
                    evaluateOptionalEquipmentResult.setStartedTime(item.getStartedTime());
                }
                if (evaluateOptionalEquipmentResult.getEndedTime().getSeconds() < item.getEndedTime().getSeconds()) {
                    evaluateOptionalEquipmentResult.setEndedTime(item.getEndedTime());
                }
                evaluateOptionalEquipmentResult.getDetails().add(new EvaluateOptionalEquipmentResult.DetailPlan(){{
                    setStartedTime(item.getStartedTime());
                    setEndedTime(item.getEndedTime());
                    setQuantity(item.getQuantity());
                }});
            }
            rst.add(evaluateOptionalEquipmentResult);
        });
        return rst;
    }

    /**
     * 添加设备评分规则
     * @param evaluateEquipment
     */
    public void addEvaluateEquipment(EvaluateEquipment evaluateEquipment){
        evaluateEquipmentList.add(evaluateEquipment);
    }

    /**
     * 完全匹配工序设备选择
     * @param task
     * @param equipments
     * @return
     */
    private List<Equipment> matchEquipment(Task task, List<Equipment> equipments) {
        return equipments;
    }

}
