package cn.iocoder.yudao.module.mes.controller.admin.plan;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils;
import cn.iocoder.yudao.module.mes.controller.admin.itemrequisition.vo.ItemRequisitionSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.*;
import cn.iocoder.yudao.module.mes.controller.admin.zjproductrecord.vo.ZjProductRecordSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.organization.OrganizationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.zjproduct.ZjProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.zjproductrecord.ZjProductRecordDO;
import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper;
import cn.iocoder.yudao.module.mes.service.itemrequisition.ItemAnalysisService;
import cn.iocoder.yudao.module.mes.service.itemrequisition.entity.ItemRequisitionAndStock;
import cn.iocoder.yudao.module.mes.service.organization.OrganizationService;
import cn.iocoder.yudao.module.mes.service.plan.PlanService;
import cn.iocoder.yudao.module.mes.service.zjproduct.ZjProductService;
import cn.iocoder.yudao.module.mes.service.zjproductrecord.ZjProductRecordService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static org.apache.ibatis.ognl.OgnlOps.getIntValue;

@Tag(name = "管理后台 - 生产计划")
@RestController
@RequestMapping("/mes/plan")
@Validated
public class PlanController {

    @Resource
    private PlanService planService;
    @Resource
    private PlanMapper planMapper;
    @Resource
    private OrganizationService organizationService;

    @Resource
    private ZjProductService zjProductService;

    @Resource
    private ZjProductRecordService zjProductRecordService;


    @PostMapping("/create")
    @Operation(summary = "创建生产计划")
    @Transactional(rollbackFor = Exception.class)
    //@PreAuthorize("@ss.hasPermission('mes:plan:create')")
    public CommonResult<Boolean> createPlan(@Valid @RequestBody PlanSaveReqVO createReqVO) {
        Long id = planService.createPlan(createReqVO);
        if (createReqVO.getIsPreProduction() != null && createReqVO.getIsPreProduction().compareTo(new BigDecimal(1)) == 0) {
            // 查对应产品的质检参数
            List<ZjProductDO> zjProductDOList = zjProductService.getZjProductByProductId(createReqVO.getProductId());
            for (ZjProductDO source : zjProductDOList) {
                ZjProductRecordDO zjProductRecordDO = new ZjProductRecordDO();
                // 属性映射
                zjProductRecordDO.setType(source.getType());
                zjProductRecordDO.setName(source.getName());
                zjProductRecordDO.setUnit(source.getUnit());
                zjProductRecordDO.setUpperVal(source.getUpperVal());
                zjProductRecordDO.setLowerVal(source.getLowerVal());
                zjProductRecordDO.setProductId(source.getProductId());
                zjProductRecordDO.setPlanId(id);
                zjProductRecordService.createZjProductRecord(BeanUtils.toBean(zjProductRecordDO, ZjProductRecordSaveReqVO.class));
            }
        }

        return success(true);
    }

    @PutMapping("/update")
    @Operation(summary = "更新生产计划")
    //@PreAuthorize("@ss.hasPermission('mes:plan:update')")
    public CommonResult<Boolean> updatePlan(@Valid @RequestBody PlanSaveReqVO updateReqVO) {
        planService.updatePlan(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除生产计划")
    @Parameter(name = "id", description = "编号", required = true)
    //@PreAuthorize("@ss.hasPermission('mes:plan:delete')")
    public CommonResult<Boolean> deletePlan(@RequestParam("id") Long id) {
        planService.deletePlan(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得生产计划")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    //@PreAuthorize("@ss.hasPermission('mes:plan:query')")
    public CommonResult<PlanRespVO> getPlan(@RequestParam("id") Long id) {
        PlanDO plan = planService.getPlan(id);
        return success(planService.getPlanRespVO(plan));
    }

    @GetMapping("/page")
    @Operation(summary = "获得生产计划分页")
    //@PreAuthorize("@ss.hasPermission('mes:plan:query')")
    public CommonResult<PageResult<PlanRespVO>> getPlanPage(@Valid PlanPageReqVO pageReqVO) {
        PageResult<PlanRespVO> pageResult = planService.getPlanPage(pageReqVO);
        return success(new PageResult<>(buildVOList(pageResult.getList()), pageResult.getTotal()));
    }

    private List<PlanRespVO> buildVOList(List<PlanRespVO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Map<Long, OrganizationDO> organizationMap = organizationService.getOrganizationVOMap(
                convertSet(list, PlanRespVO::getFeedingPipeline));
        return BeanUtils.toBean(list, PlanRespVO.class, item -> {
            MapUtils.findAndThen(organizationMap, item.getFeedingPipeline(), organization -> item.setFeedingPipelineName(organization.getName()));
        });
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出生产计划 Excel")
    //@PreAuthorize("@ss.hasPermission('mes:plan:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportPlanExcel(@Valid PlanPageReqVO pageReqVO,
                                HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<PlanRespVO> list = planService.getPlanPage(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "生产计划.xls", "数据", PlanRespVO.class, list);
    }

    @PostMapping("/saveBatchPlan")
    @Operation(summary = "批量新增计划")
    public CommonResult<Boolean> saveBatchPlan(@RequestBody List<PlanDO> planDOList) {
        Boolean result = planService.saveBatchPlan(planDOList);
        if (result)
            return success(result);
        else
            return error(500, "insert error");
    }

    @Resource
    private ItemAnalysisService analysisService;

    @PutMapping("/paigong")
    @Operation(summary = "下料派工")
    //@PreAuthorize("@ss.hasPermission('mes:plan:update')")
    public CommonResult<Boolean> updatePlan(@Valid @RequestBody ItemRequisitionSaveReqVO saveReqVO) {
        PlanDO planDO = planMapper.selectById(saveReqVO.getPlanId());
        List<ItemRequisitionAndStock> list = analysisService.getItemAnalysis(planDO);
        planService.arrangePlan(saveReqVO, planDO, list);
        return success(true);
    }

    @PutMapping("/typePaigong")
    @Operation(summary = "工序派工")
    //@PreAuthorize("@ss.hasPermission('mes:plan:update')")
    public CommonResult<Boolean> updatePlanNum(@Valid @RequestBody ItemRequisitionSaveReqVO saveReqVO) {
        PlanDO planDO = planMapper.selectById(saveReqVO.getPlanId());
        //List<ItemRequisitionAndStock> list = analysisService.getItemAnalysis(planDO);
        planService.arrangePlanNum(saveReqVO, planDO);
        return success(true);
    }

    @PutMapping("/updateStatus")
    @Operation(summary = "更新生产计划状态")
    //@PreAuthorize("@ss.hasPermission('mes:plan:update')")
    public CommonResult<Boolean> updatePlanStatus(@Valid @RequestBody PlanStatusUpdateVO statusUpdateVO) {
        PlanDO planDO = planMapper.selectById(statusUpdateVO.getId());
        // 试产
        if ("pre".equals(statusUpdateVO.getCode())) {
            planDO.setStatus(PlanStatusEnum.试产.getValue());
        }
        //开工
        if ("start".equals(statusUpdateVO.getCode())) {
            planDO.setStatus(PlanStatusEnum.量产.getValue());
        }
        //暂停
        else if ("pause".equals(statusUpdateVO.getCode())) {
            planDO.setStatus(PlanStatusEnum.暂停.getValue());
        }
        //完工
        else if ("end".equals(statusUpdateVO.getCode())) {
            planDO.setStatus(PlanStatusEnum.完工.getValue());
        }
        // 入库
        else if ("store".equals(statusUpdateVO.getCode())) {
            planDO.setStatus(PlanStatusEnum.已入库.getValue());
            planService.createPlanStockIn(statusUpdateVO, planDO);
        }
        planMapper.updateById(planDO);
        return success(true);
    }

    @PutMapping("/updatePlanZjStatus")
    @Operation(summary = "更新生产计划质检状态")
    //@PreAuthorize("@ss.hasPermission('mes:plan:update')")
    public CommonResult<Boolean> updatePlanZjStatus(@Valid @RequestBody PlanStatusUpdateVO statusUpdateVO) {
        PlanDO planDO = planMapper.selectById(statusUpdateVO.getId());
        planDO.setIsZj(true);
        planMapper.updateById(planDO);
        return success(true);
    }

    @GetMapping("/getProductByStatus")
    @Operation(summary = "获得正在开工生产的产品")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    //@PreAuthorize("@ss.hasPermission('mes:plan:query')")
    public CommonResult<List<ErpProductRespVO>> getPlan() {
        List<Integer> status = new ArrayList<>();
        status.add(PlanStatusEnum.量产.getValue());
        //status.add(PlanStatusEnum.完工.getValue());
        List<ErpProductRespVO> proList = planService.getProductByPlanStatus(status);
        return success(proList);
    }

    @GetMapping("/getByStatus")
    @Operation(summary = "获得生产计划")
    @Parameter(name = "status", description = "状态", required = true, example = "1024")
    public CommonResult<List<PlanRespVO>> getByStatus(@RequestParam("status") Integer status) {
        List<Integer> statusList = new ArrayList<>();
        statusList.add(status);
        //statusList.add(PlanStatusEnum.开工.getValue());
//        statusList.add(PlanStatusEnum.派工.getValue());
//        statusList.add(PlanStatusEnum.计划.getValue());
//        statusList.add(PlanStatusEnum.暂停.getValue());
        List<PlanDO> proList = planService.getPlanByStatus(statusList);
        List<PlanRespVO> planRespVOList = planService.buildVOList(proList);
        return success(planRespVOList);
    }

    @GetMapping("/getProductPlans")
    @Operation(summary = "获得生产计划")
    @Parameter(name = "status", description = "状态", required = true, example = "1024")
    public CommonResult<List<PlanRespVO>> getProductPlans() {
        List<Integer> statusList = new ArrayList<>();
        statusList.add(PlanStatusEnum.试产.getValue());
        statusList.add(PlanStatusEnum.量产.getValue());
        statusList.add(PlanStatusEnum.暂停.getValue());
        statusList.add(PlanStatusEnum.待入库.getValue());
        //statusList.add(PlanStatusEnum.开工.getValue());
//        statusList.add(PlanStatusEnum.派工.getValue());
//        statusList.add(PlanStatusEnum.计划.getValue());
//        statusList.add(PlanStatusEnum.暂停.getValue());
        List<PlanDO> proList = planService.getPlanByStatus(statusList);
        List<PlanRespVO> planRespVOList = planService.buildVOList(proList);
        return success(planRespVOList);
    }

    @GetMapping("/getByTicketType")
    @Operation(summary = "获得质检工单下拉")
    @Parameter(name = "status", description = "状态", required = true, example = "1024")
    public CommonResult<List<PlanRespVO>> getByTicketType(@RequestParam("status") Integer status) {
        if (status == 1) {
            List<Integer> statusList = new ArrayList<>();
            statusList.add(status);
            statusList.add(PlanStatusEnum.已排产.getValue());
            statusList.add(PlanStatusEnum.试产.getValue());
            statusList.add(PlanStatusEnum.量产.getValue());
            statusList.add(PlanStatusEnum.暂停.getValue());
            List<PlanDO> proList = planService.getPlanByStatus(statusList);
            List<PlanRespVO> planRespVOList = planService.buildVOList(proList);
            return success(planRespVOList);
        } else if (status == 2) {
            List<Integer> statusList = new ArrayList<>();
            statusList.add(PlanStatusEnum.待入库.getValue());
            List<PlanDO> proList = planService.getPlanByStatus(statusList);
            List<PlanRespVO> planRespVOList = planService.buildVOList(proList);
            return success(planRespVOList);
        } else {
            return success(Collections.emptyList());
        }
    }

    @GetMapping("/setTop")
    @Operation(summary = "置顶生产计划")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    public CommonResult<Boolean> setTop(@RequestParam("id") Long id) {
        PlanDO plan = planService.getPlan(id);
        plan.setPriorityNum(DateUtils.getMillsLong());
        planMapper.updateById(plan);
        return success(true);
    }

    @GetMapping("/getWeekTrend")
    @Operation(summary = "综合大屏-周生产趋势")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    public CommonResult<PlanWeekTrendVO> getWeekTrend() {
        PlanWeekTrendVO planWeekTrendVO = new PlanWeekTrendVO();
        LocalDate today = LocalDate.now();
        // 获取本周一
        LocalDate start = today.with(DayOfWeek.MONDAY);
        // 获取本周日
        LocalDate end = today.with(DayOfWeek.SUNDAY);

        // 转换为 Date
        Date monday = Date.from(start.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date sunday = Date.from(end.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());
        List<Map<String, Object>> result = planMapper.selectMaps(
                new QueryWrapper<PlanDO>()
                        .select(
                                "DATE(create_time) as date",
                                "DAYOFWEEK(create_time) as weekday",
                                "SUM(plan_number) as planCount",
                                "SUM(wangong_number) as wangongCount"
                        )
                        .ge("create_time", monday)
                        .le("create_time", sunday)
                        .groupBy("DATE(create_time), DAYOFWEEK(create_time)")
                        .orderByAsc("DATE(create_time)")
        );
        List<String> weekDays = new  ArrayList<>(Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日"));
        List<Integer> weekPlan = new ArrayList<>(Collections.nCopies(7, 0));
        List<Integer> weekReal = new ArrayList<>(Collections.nCopies(7, 0));
        for (Map<String, Object> map : result) {
            Object weekdayObj = map.get("weekday");
            // 确定星期字符串
            String weekdayStr = null;
            if (weekdayObj instanceof Integer) {
                int dayCode = (Integer) weekdayObj;
                weekdayStr = WeekDayEnum.getNameByCode(dayCode);  // 使用枚举转换
            } else if (weekdayObj instanceof String) {
                weekdayStr = (String) weekdayObj;
            } else if (weekdayObj != null) {
                weekdayStr = weekdayObj.toString();
            }
            // 匹配并填充数据
            if (weekdayStr != null) {
                int index = weekDays.indexOf(weekdayStr);
                if (index != -1) {
                    int plan = Math.max(getIntValue(map.get("planCount")), 0);
                    int real = Math.max(getIntValue(map.get("wangongCount")), 0);

                    weekPlan.set(index, plan);
                    weekReal.set(index, real);
                    weekReal.set(index, real);
                }
            }

        }
        planWeekTrendVO.setWeekDays(weekDays);
        planWeekTrendVO.setWeekPlan(weekPlan);
        planWeekTrendVO.setWeekReal(weekReal);
        return success(planWeekTrendVO);
    }

    @GetMapping("/getPlanCapacity")
    @Operation(summary = "综合大屏-日/月产能达成情况")
    @Parameter(name = "type", description = "类型 1-日 2-月", required = true, example = "1024")
    public CommonResult<PlanCapacityVO> getPlanCapacity(
            @RequestParam(name = "type", defaultValue = "1") Integer type) {

        PlanCapacityVO vo = new PlanCapacityVO();

        //  根据 type 计算时间区间
        LocalDate today = LocalDate.now();
        LocalDateTime startTime;
        LocalDateTime endTime;

        if (type != null && type == 2) {
            // 月
            startTime = today.withDayOfMonth(1).atStartOfDay();
            endTime = today.with(TemporalAdjusters.lastDayOfMonth())
                    .atTime(LocalTime.MAX);
        } else {
            // 日（默认）
            startTime = today.atStartOfDay();
            endTime = today.atTime(LocalTime.MAX);
        }

        // 一次性查询该时间段内所有计划
        List<PlanDO> planList =
                planService.getPlanCapacity(null, startTime, endTime);

        long orders = 0;
        long planTotal = 0;
        long finishedTotal = 0;
        long totalPass = 0;
        long totalFinish = 0;


        for (PlanDO planDO : planList) {

            Integer status = planDO.getStatus();

            // 已排产
            if (Objects.equals(status, PlanStatusEnum.已排产.getValue())) {
                orders++;
                if (planDO.getPlanNumber() != null) {
                    planTotal += planDO.getPlanNumber();
                }
            }

            // 已生产（已入库 + 待入库）
            if (Objects.equals(status, PlanStatusEnum.已入库.getValue())
                    || Objects.equals(status, PlanStatusEnum.待入库.getValue())) {
                if (planDO.getWangongNumber() != null) {
                    finishedTotal += planDO.getWangongNumber();
                }
            }

            // 产率（全部计划）
            if (planDO.getPassNumber() != null) {
                totalPass += planDO.getPassNumber();
            }
            if (planDO.getWangongNumber() != null) {
                totalFinish += planDO.getWangongNumber();
            }
        }

        // 设置返回值
        vo.setOrders((int) orders);
        vo.setPlan((int) planTotal);
        vo.setPending((int) finishedTotal);

        // 产率（百分比）
        double rate = 0D;
        if (totalFinish > 0) {
            rate = BigDecimal.valueOf(totalPass)
                    .multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(totalFinish), 2, RoundingMode.HALF_UP)
                    .doubleValue();
        }
        vo.setRate(rate);

        return success(vo);
    }



    @GetMapping("/getLastDaysRate")
    @Operation(summary = "综合大屏-前7天每日产率（百分比）")
    public CommonResult<Map<String, Double>> getLastDaysRate() {

        Map<String, Double> result = new LinkedHashMap<>();

        LocalDate today = LocalDate.now();

        // 遍历前七天
        for (int i = 6; i >= 0; i--) {  // 从 6 天前到今天
            LocalDate date = today.minusDays(i);
            LocalDateTime startTime = date.atStartOfDay();
            LocalDateTime endTime = date.atTime(LocalTime.MAX);

            // 查询当天的所有计划
            List<PlanDO> planList = planService.getPlanCapacity(null, startTime, endTime);

            long totalPass = 0;
            long totalFinish = 0;

            for (PlanDO planDO : planList) {
                if (planDO.getPassNumber() != null) totalPass += planDO.getPassNumber();
                if (planDO.getWangongNumber() != null) totalFinish += planDO.getWangongNumber();
            }

            double rate = 0D;
            if (totalFinish > 0) {
                rate = BigDecimal.valueOf(totalPass)
                        .multiply(BigDecimal.valueOf(100))
                        .divide(BigDecimal.valueOf(totalFinish), 2, RoundingMode.HALF_UP)
                        .doubleValue();
            }

            // key = yyyy-MM-dd
            result.put(date.toString(), rate);
        }

        return success(result);
    }


    @GetMapping("/getLastSevenDaysCompletedCount")
    @Operation(summary = "获取过去7天每天完工数量统计")
    @PreAuthorize("@ss.hasPermission('mes:plan:query')")
    @Parameter(name = "orgId", description = "产线组织Id", required = true, example = "1024")
    public CommonResult<List<Map<String, Object>>> getLastSevenDaysCompletedCount( @RequestParam(name = "orgId" ,required = false) Long orgId) {
        List<Map<String, Object>> dayCounts = planService.getLastSevenDaysCompletedCount();
        return success(dayCounts);
    }
}

