package com.jwipc.order.logic.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.jwipc.constant.DelFlag;
import com.jwipc.constant.EnableStatus;
import com.jwipc.constant.FlagConst;
import com.jwipc.exception.DaoException;
import com.jwipc.order.constant.AppointmentStatus;
import com.jwipc.order.convert.AppointmentRecordConvert;
import com.jwipc.order.dto.*;
import com.jwipc.order.entity.*;
import com.jwipc.order.logic.AppointmentRecordLogic;
import com.jwipc.order.logic.AppointmentTimeLogic;
import com.jwipc.order.model.po.AppointmentRecordPO;
import com.jwipc.order.model.po.AppointmentShowMainPO;
import com.jwipc.order.model.po.ArrivalControlConfigPO;
import com.jwipc.order.model.po.DeliveryDetailRecordPO;
import com.jwipc.order.model.query.AppointmentDockerQuery;
import com.jwipc.order.model.query.AppointmentRecordQuery;
import com.jwipc.order.model.query.AppointmentShowTimeQuery;
import com.jwipc.order.service.*;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.security.LoginUtil;
import com.jwipc.sys.client.SequenceClient;
import com.jwipc.sys.client.SysConfigClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * </p>
 *
 * @author wt
 * @version 1.0
 * @since 2024/3/5
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AppointmentRecordLogicImpl implements AppointmentRecordLogic {

    private final IAppointmentRecordService appointmentRecordService;
    private final AppointmentRecordConvert appointmentRecordConvert;
    private final IAppointmentSupplierService appointmentSupplierService;

    private final IAppointmentDayOffService appointmentDayOffService;
    private final AppointmentTimeLogic appointmentTimeLogic;
    private final IAppointmentTimeService appointmentTimeService;
    private final IAppointmentDockerService appointmentDockerService;

    private final IArrivalControlConfigService arrivalControlConfigService;

    private final SysConfigClient sysConfigClient;

    private final SequenceClient sequenceClient;
    private final IDeliveryOrderService deliveryOrderService;
    private final IDeliveryOrderDetailService deliveryOrderDetailService;

    @Override
    public PageView getList(AppointmentRecordQuery query) {
        if (LoginUtil.isSupplier()) {
            query.setLoginSupplierCode(LoginUtil.getSupplierCode());
        } else {
            query.setUserId(LoginUtil.getUserId());
        }

        final Page<AppointmentRecord> dbPage = PageUtil.toPage(query);
        List<AppointmentRecordPO> poList = appointmentRecordService.findPageByQuery(dbPage, query);

        List<AppointmentRecordListDTO> dtoList = appointmentRecordConvert.po2PageListDTO(poList);
        return PageUtil.getPV(dtoList, dbPage.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void devOrderSave(DevAppointmentParamDTO paramDTO) throws Exception
    {
        final Long id = paramDTO.getId();
        final Long appointmentId = paramDTO.getAppointmentId();
        final String selectDay = paramDTO.getSelectDay();
        final String dockerCode = paramDTO.getDockerCode();
        final String deliveryIds = paramDTO.getDeliveryIds();
        final String factoryCode = paramDTO.getFactoryCode();
        final Integer urgencyFlag = paramDTO.getUrgencyFlag();
        appointmentDayOffService.checkDay(selectDay);
        List<String> deleiverys = Arrays.asList(deliveryIds.split(","));
        AppointmentTime2DTO appointmentTimeDto = appointmentTimeLogic.getTimeRangeCouldNum(selectDay,
                String.valueOf(appointmentId), dockerCode, factoryCode);
        Integer surplusNum = appointmentTimeDto.getSurplusNum();
        //普通预约进行数量管控
        if (Objects.equals(urgencyFlag, FlagConst.N.getCode())) {
            if (surplusNum <= 0) {
                throw new DaoException("暂无可预约数量");
            }
            if (Objects.isNull(id)) {
                Long detailsSize = deliveryOrderDetailService.count(
                        new LambdaQueryWrapper<DeliveryOrderDetail>().eq(DeliveryOrderDetail::getDelFlag,
                                DelFlag.NORMAL.getStatus()).in(DeliveryOrderDetail::getOrderId, deleiverys));
                if (detailsSize == 0) {
                    throw new DaoException("送货单没有送货明细");
                }
            }
        } else {
            AppointmentDockerQuery query = new AppointmentDockerQuery();
            query.setDockerCode(dockerCode);
            query.setFactory(factoryCode);
            List<AppointmentDocker> appointmentDockers = appointmentDockerService.queryByPage(null, query);
            if (CollectionUtil.isEmpty(appointmentDockers) || appointmentDockers.size() > 1) {
                throw new DaoException(
                        "工厂编码：" + factoryCode + "---码头：" + dockerCode + "没有配置或配置多个绿色放行数");
            }

            AppointmentDocker appointmentDocker = appointmentDockers.get(0);
            BigDecimal urgentNum = appointmentDocker.getUrgentNum();
            List<AppointmentRecord> records = appointmentRecordService.list(
                    new LambdaQueryWrapper<AppointmentRecord>().eq(AppointmentRecord::getAppointmentDate,
                                    LocalDateTimeUtil.parseDate(selectDay, "yyyy-MM-dd"))
                            .eq(AppointmentRecord::getUrgencyFlag, 1).eq(AppointmentRecord::getStatus, 1));
            if (records.size() >= urgentNum.intValue()) {
                throw new DaoException(selectDay + "绿色放行阈值" + urgentNum.intValue() + "已用完，请重新预约！");
            }
        }
        boolean b = appointmentTimeService.checkAppointmentTime(selectDay, appointmentId);
        if (!b) {
            throw new DaoException("预约时间段无效");
        }
        this.save(selectDay, appointmentId, deleiverys, urgencyFlag, factoryCode, dockerCode, id,
                paramDTO.getSupplierName(), paramDTO.getSupplierCode());
    }

    @Override
    public void save(String selectDay, Long appointmentId, List<String> deleiverys, Integer urgencyFlag,
                     String factoryCode, String dockerCode, Long id, String supplierName, String supplierCode)
    {
        AppointmentTime appointmentTime = appointmentTimeService.getById(appointmentId);
        AppointmentRecord record = new AppointmentRecord();
//        String demoDeliveryCode = sequenceClient.createSequenceNo("demo_delivery_code");
        String demoDeliveryCode = sequenceClient.getBarCodeSequenceNo("demo_delivery_code", 6, false);
        if (Objects.nonNull(id)) {
            final AppointmentRecord dbRecord = appointmentRecordService.getById(id);
            record.setId(id);

            if (Objects.equals(urgencyFlag, FlagConst.Y.getCode())) {
                record.setUrgencyFlag(1);
                record.setStatus(AppointmentStatus.APPOINTMENT_STATUS_0.getCode());
            } else {
                record.setStatus(AppointmentStatus.APPOINTMENT_STATUS_1.getCode());
            }
            record.setMaxNum(appointmentTime.getMaxNum());
            record.setAppointmentTimeId(appointmentId);
            record.setAppointmentStartTime(appointmentTime.getStartTime());
            record.setAppointmentEndTime(appointmentTime.getEndTime());

            record.setAppointmentDate(LocalDateTimeUtil.parseDate(selectDay, "yyyy-MM-dd"));
            record.setLastAppointmentId(dbRecord.getAppointmentTimeId());
            record.setLastDate(dbRecord.getAppointmentDate());
            record.setLastEndTime(dbRecord.getAppointmentEndTime());
            record.setLastStartTime(dbRecord.getAppointmentStartTime());
            record.setUpdateTime(LocalDateTime.now());
            appointmentRecordService.updateById(record);

            final LambdaUpdateWrapper<DeliveryOrder> ined = new LambdaUpdateWrapper<DeliveryOrder>().set(
                    DeliveryOrder::getAppointmentCode, demoDeliveryCode).in(DeliveryOrder::getId, deleiverys);
            if (record.getStatus().equals(AppointmentStatus.APPOINTMENT_STATUS_1.getCode())) {
                ined.set(DeliveryOrder::getPrintLock, FlagConst.N.getCode());
            } else {
                ined.set(DeliveryOrder::getPrintLock, FlagConst.Y.getCode());
            }
            deliveryOrderService.update(ined);
        } else {
            record.setType(1);
            record.setFactory(factoryCode);
            record.setDockerCode(dockerCode);
            if (Objects.equals(urgencyFlag, FlagConst.Y.getCode())) {
                record.setUrgencyFlag(1);
                record.setStatus(AppointmentStatus.APPOINTMENT_STATUS_0.getCode());
            } else {
                record.setStatus(AppointmentStatus.APPOINTMENT_STATUS_1.getCode());
            }
            record.setAppointmentCode(demoDeliveryCode);
            record.setSupplierCode(supplierCode);
            record.setSupplierName(supplierName);
            record.setMaxNum(appointmentTime.getMaxNum());
            record.setAppointmentTimeId(appointmentId);
            record.setAppointmentStartTime(appointmentTime.getStartTime());
            record.setAppointmentEndTime(appointmentTime.getEndTime());
            record.setAppointmentDate(LocalDateTimeUtil.parseDate(selectDay, "yyyy-MM-dd"));
            record.setCreateTime(LocalDateTime.now());
            appointmentRecordService.save(record);
            final LambdaUpdateWrapper<DeliveryOrder> ined = new LambdaUpdateWrapper<DeliveryOrder>().set(
                    DeliveryOrder::getAppointmentCode, demoDeliveryCode).in(DeliveryOrder::getId, deleiverys);
            if (record.getStatus().equals(AppointmentStatus.APPOINTMENT_STATUS_1.getCode())) {
                ined.set(DeliveryOrder::getPrintLock, FlagConst.N.getCode());
            } else {
                ined.set(DeliveryOrder::getPrintLock, FlagConst.Y.getCode());
            }
            deliveryOrderService.update(ined);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void devOrderChange(String selectDay, String appointmentId, String ids, String dockerCode,
                               String factoryCode) throws Exception
    {
        AppointmentTime2DTO dto = appointmentTimeLogic.getTimeRangeCouldNum(selectDay, appointmentId, dockerCode,
                factoryCode);
        Integer surplusNum = dto.getSurplusNum();
        if (surplusNum <= 0) {
            throw new DaoException("暂无可预约数量");
        }
        List<DeliveryOrder> details = deliveryOrderService.selectDetailsByAppointmentIds(Arrays.asList(ids.split(",")));
        if (CollectionUtil.isEmpty(details)) {
            throw new DaoException("送货单没有送货明细");
        }
        if (CollectionUtil.isEmpty(details)) {
            throw new DaoException("送货单没有送货明细");
        }
        int size = details.size();
        if (size > surplusNum) {
            throw new DaoException("送货单明细行数【" + size + "行】大于当前时间段的最大可预约数【" + surplusNum + "】");
        }
        boolean b = appointmentTimeService.checkAppointmentTime(selectDay, Long.parseLong(appointmentId));
        if (!b) {
            throw new DaoException("预约时间段无效");
        }
        appointmentDayOffService.checkDay(selectDay);
        this.changeDate(Arrays.asList(ids.split(",")), appointmentId, selectDay, dto);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatchIds(List<String> ids) {
        final List<AppointmentRecord> appointmentRecords = appointmentRecordService.listByIds(ids);
        final Set<String> codes = appointmentRecords.stream().map(AppointmentRecord::getAppointmentCode)
                .collect(Collectors.toSet());
        appointmentRecordService.removeBatchByIds(ids);
        if (CollUtil.isNotEmpty(codes)) {
            // 删除送货单明细
            final List<DeliveryOrder> list = deliveryOrderService.list(
                    new LambdaQueryWrapper<DeliveryOrder>().in(DeliveryOrder::getAppointmentCode, codes));
            final Set<String> supplierCodes = list.stream().map(DeliveryOrder::getSupplierErpCode)
                    .collect(Collectors.toSet());
            final List<AppointmentSupplier> list1 = appointmentSupplierService.list(
                    new LambdaQueryWrapper<AppointmentSupplier>().in(AppointmentSupplier::getSupplierCode,
                                    supplierCodes)
                            .eq(AppointmentSupplier::getDeleteFlag, DelFlag.NORMAL.getStatus())
                            .eq(AppointmentSupplier::getStatus, EnableStatus.ENABLE.getStatus()));
            list1.stream().map(a -> a.getSupplierCode()).forEach(a -> supplierCodes.remove(a));//去除预约供应商
            final Set<Long> orderIds = list.stream().map(DeliveryOrder::getId).collect(Collectors.toSet());

            if (CollUtil.isNotEmpty(supplierCodes)) {
                final List<Long> update1 = list.stream()
                        .filter(a -> supplierCodes.contains(a.getSupplierErpCode())).map(DeliveryOrder::getId).toList();
                //不是预约配置供应商
                if (CollUtil.isNotEmpty(update1)) {
                    final LambdaUpdateWrapper<DeliveryOrder> updateWrapper =
                            new LambdaUpdateWrapper<DeliveryOrder>().set(
                                            DeliveryOrder::getAppointmentCode, "")
                                    .set(DeliveryOrder::getPrintLock, FlagConst.N.getCode())
                                    .in(DeliveryOrder::getId, update1);
                    deliveryOrderService.update(updateWrapper);
                }
                final List<Long> update2 = list.stream()
                        .filter(a -> !supplierCodes.contains(a.getSupplierErpCode())).map(DeliveryOrder::getId)
                        .toList();
                //是预约配置供应商
                if (CollUtil.isNotEmpty(update2)) {
                    final LambdaUpdateWrapper<DeliveryOrder> updateWrapper =
                            new LambdaUpdateWrapper<DeliveryOrder>().set(
                                            DeliveryOrder::getAppointmentCode, "")
                                    .set(DeliveryOrder::getPrintLock, FlagConst.N.getCode())
                                    .in(DeliveryOrder::getId, update2);
                    deliveryOrderService.update(updateWrapper);
                }
            } else {
                final LambdaUpdateWrapper<DeliveryOrder> updateWrapper = new LambdaUpdateWrapper<DeliveryOrder>().set(
                                DeliveryOrder::getAppointmentCode, "")
                        .set(DeliveryOrder::getPrintLock, FlagConst.Y.getCode()).in(DeliveryOrder::getId, orderIds);
                deliveryOrderService.update(updateWrapper);
            }
        }

    }

    @Override
    public AppointmentShowMainDTO appointmentShow(AppointmentShowTimeQuery query) {
        query.setOperateId(LoginUtil.getUserId());
        String appointmentDate = query.getAppointmentDate();
        if (StringUtils.isEmpty(appointmentDate)) {
            appointmentDate = DateUtil.today();
        }
        int weekOfDay = getWeekOfDay(appointmentDate);
        query.setWeekOfDay(weekOfDay);
        AppointmentShowMainPO showMainPO = appointmentRecordService.appointmentShow(query);
        return appointmentRecordConvert.poToDtoSHowMainList(showMainPO);
    }

    @Override
    public PageView deliveryDetailsRecord(AppointmentRecordQuery query) {
        final Page<AppointmentRecord> dbPage = PageUtil.toPage(query);
        String ids = query.getIds();
        if (ObjectUtil.isNotNull(ids)) {
            query.setRecordIds(Arrays.asList(ids.split(",")));
        }
        if (LoginUtil.isSupplier()) {
            query.setSupplierCode(LoginUtil.getSupplierCode());
        }
        List<DeliveryDetailRecordPO> poList = appointmentRecordService.deliveryDetailsRecord(dbPage, query);

        List<DeliveryDetailRecordDTO> dtoList = appointmentRecordConvert.poDetail2PageListDTO(poList);
        return PageUtil.getPV(dtoList, dbPage.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pass(String id) {
        final AppointmentRecord record = appointmentRecordService.getById(id);
        if (ObjectUtil.isNull(record)) {
            throw new DaoException("未找到预约记录");
        }
        record.setStatus(AppointmentStatus.APPOINTMENT_STATUS_1.getCode());
        appointmentRecordService.updateById(record);
        //需要修改送货单的打印锁定
        final LambdaUpdateWrapper<DeliveryOrder> updateWrapper = new LambdaUpdateWrapper<DeliveryOrder>().set(
                        DeliveryOrder::getPrintLock, FlagConst.N.getCode())
                .eq(DeliveryOrder::getAppointmentCode, record.getAppointmentCode());
        deliveryOrderService.update(updateWrapper);

    }

    private void changeDate(List<String> ids, String appointmentId, String selectDay, AppointmentTime2DTO dto) {
        List<DeliveryOrder> deliveryOrders = Lists.newArrayList();
        AppointmentTime appointmentTime = new AppointmentTime();

        List<AppointmentRecord> records = appointmentRecordService.listByIds(ids);
        appointmentTime.setStartTime(dto.getOldStartTime());
        appointmentTime.setEndTime(dto.getOldStartTime());
//        int printLock = checkNeedLock(selectDay, "0", appointmentTime);

        List<String> deliveryIds = records.stream().map(t -> String.valueOf(t.getId())).collect(Collectors.toList());
        Map<Long, Integer> map = checkNeedLockV2(deliveryIds, "0", appointmentTime, selectDay);

        records.forEach(t -> {
            int printLock = 0;
            Long deliveryId = t.getDeliveryId();
            if (t.getType() == 1) {
                printLock = ObjectUtil.isNull(map.get(deliveryId)) ? 0 : map.get(deliveryId);
            }
            Long oldAppointmentId = t.getAppointmentTimeId();
            String oldStartTime = t.getAppointmentStartTime();
            String oldEndTime = t.getAppointmentEndTime();
            LocalDate oldAppointmentData = t.getAppointmentDate();
            t.setLastAppointmentId(oldAppointmentId);
            t.setLastStartTime(oldStartTime);
            t.setLastEndTime(oldEndTime);
            t.setLastDate(oldAppointmentData);
            t.setAppointmentTimeId(dto.getId());
            t.setAppointmentStartTime(dto.getOldStartTime());
            t.setAppointmentEndTime(dto.getOldEndTime());
            t.setAppointmentDate(LocalDateTimeUtil.parseDate(selectDay, "yyyy-MM-dd"));
            t.setMaxNum(dto.getMaxNum());
            t.setUpdateTime(LocalDateTime.now());
            t.setUrgencyFlag(0);

            DeliveryOrder order = new DeliveryOrder();
            order.setId(t.getDeliveryId());
            order.setUrgencyFlag(0);//绿色放行的预约，变更则为普通预约
            order.setUrgencyUseName("");
            order.setPrintLock(printLock);
            deliveryOrders.add(order);
        });
        appointmentRecordService.updateBatchById(records);
        deliveryOrderService.updateBatchById(deliveryOrders);
    }

    /**
     * V2 版本逻辑： 校验选择的日期是否需要自动上锁
     * 根据送货单 找对于的明细行匹配dev_arrival_control_config表的到货周期数据（多个取最长的）
     * 没有配置则取系统配置项的默认数据
     * 当前日期在预约日期-到货周期 前则锁定；
     *
     * @param deliveryIds     送货单IDS
     * @param urgencyFlag     绿色放行标记0否 1是
     * @param appointmentTime 选择的时间段
     * @param selectDay       选择的日期
     * @return 送货单ID 对于是否锁定 0否 1 是
     */
    public Map<Long, Integer> checkNeedLockV2(List<String> deliveryIds, String urgencyFlag,
                                              AppointmentTime appointmentTime, String selectDay)
    {
        String defaultCargoWeek = sysConfigClient.getTextConfig("default_cargo_week");
        if (StringUtils.equals(urgencyFlag, "0")) {
            List<ArrivalControlConfigPO> list = arrivalControlConfigService.selectCargoWeek(deliveryIds,
                    defaultCargoWeek);
            Map<Long, Integer> map = new HashMap<>();
            String startTime = appointmentTime.getStartTime();
            list.forEach(t -> {
                Long deliveryId = t.getDeliveryId();
                Integer cargoWeek = t.getCargoWeek();
                int printLock = 1;
                DateTime couldEndTime = DateUtil.offsetDay(DateUtil.parse(selectDay, DatePattern.NORM_DATE_FORMAT),
                        -(cargoWeek + 1));
                DateTime now = DateUtil.parse(DateUtil.now(), DatePattern.NORM_DATE_FORMAT);
                if (now.compareTo(couldEndTime) > 0) {
                    printLock = 0;
                }
                map.put(deliveryId, printLock);
            });
            return map;
        }
        return new HashMap<>();
    }

    /**
     * 获取日期对应的周几  和系统配置表中的item_value相匹配
     *
     * @param day 日期
     * @return item_value
     */
    private int getWeekOfDay(String day) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(DateUtil.parse(day));
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }

        return w + 1;
    }
}
