package com.engine.htsc.trip.service.impl;


import com.alibaba.fastjson.JSON;
import com.engine.core.impl.Service;
import com.engine.htsc.trip.pojo.AllowanceRequest;
import com.engine.htsc.trip.pojo.JourneyEntity;
import com.engine.htsc.trip.pojo.OrderEntity;
import com.engine.htsc.trip.service.AllowanceService;
import com.google.common.collect.Lists;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;


public class AllowanceServiceImpl extends Service implements AllowanceService {

  private static final String TITLE = "doGetAllowance";
  private Log log = LogFactory.getLog(this.getClass());
  private static final DateTimeFormatter FORMATTER = DateTimeFormatter
      .ofPattern("yyyy-MM-dd HH:mm:ss");

  @Override
  public Map<String, Object> execute(AllowanceRequest params) {
    Map<String, Object> result = new HashMap<>();
    //此阶段明细表尚无数据
    String type = params.getType();
    String isLeader = params.getIsLeader();
    String travelApplyNo = params.getTravelApplyNo();
    List<JourneyEntity> journeyList = params.getJourneyList();
    if (CollectionUtils.isEmpty(journeyList)) {
      log.warn(String.format("%s journey list is empty! applyno:%s", TITLE, travelApplyNo));
      return result;
    }
    journeyList.sort(Comparator.comparing(JourneyEntity::getBeginTime));
    int allowance = 0;
    result.put("applyNo", travelApplyNo);
    String firstDay = "";
    String departure = "";
    Map<String, String> dayMap = new HashMap<>();
    for (int i = 0; i < journeyList.size(); i++) {
      JourneyEntity journey = journeyList.get(i);
      List<String> dayList = getDateList(journey.getBeginTime(), journey.getEndTime());
      if (i == 0) {
        departure = journey.getBeginCity();
        firstDay = dayList.get(0);
      }
      dayList.forEach(f -> {
        dayMap.put(f, journey.getEndCity());
      });
    }
    LocalDate firstLocalDate = LocalDate.parse(firstDay, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    //带领导报销
    if ("true".equals(isLeader)) {

      for (Entry<String, String> entry : dayMap.entrySet()) {
        String k = entry.getKey();
        String v = entry.getValue();
        allowance += getStandardByCity(v);
      }
    } else {
      if ("1".equals(type)) {
        //内部会议出差
        allowance = 0;
      } else if ("2".equals(type)) {
        //外部会议培训
        allowance = 50 * dayMap.size();
      } else {
        //单日行程
        if (dayMap.size() == 1) {
          //使用当天的目的地城市作为补贴标准城市
          allowance = getStandardByCity(dayMap.entrySet().iterator().next().getValue());
        } else {
          List<OrderEntity> orderList = params.getOrderList();
          if (CollectionUtils.isEmpty(orderList) || orderList.size() < 2) {
            log.warn(String
                .format("%s order list is empty or size < 2 ! applyno:%s", TITLE, travelApplyNo));
          } else {
            //整理每日订单
            Map<String, List<OrderEntity>> dayOrderMap = new HashMap<>();
            String lastOrderDay = "";
            orderList.sort(Comparator.comparing(OrderEntity::getBeginTime));
            for (int i = 0; i < orderList.size(); i++) {
              OrderEntity order = orderList.get(i);
              if (i == orderList.size() - 1) {
                lastOrderDay = order.getEndTime();
              }
              String key = order.getBeginTime().substring(0, 10);
              if (dayOrderMap.containsKey(key)) {
                dayOrderMap.get(key).add(order);
              } else {
                List<OrderEntity> arr = new ArrayList<>();
                arr.add(order);
                arr.sort(Comparator.comparing(OrderEntity::getBeginTime));
                dayOrderMap.put(key, arr);
              }
            }

            LocalDate lastLocalDate = LocalDate
                .parse(lastOrderDay, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            //遍历每一天行程
            for (Entry<String, String> entry : dayMap.entrySet()) {
              String k = entry.getKey();
              LocalDate date = LocalDate.parse(k, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
              if (date.isAfter(lastLocalDate)) {
                continue;
              }
              List<OrderEntity> dayOrderArr = dayOrderMap.get(k);
              //判断是否为最后一天
              if (k.equals(lastLocalDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))) {
                //如果最后一天没有订单则该天差补为0
                if (CollectionUtils.isEmpty(dayOrderArr)) {
                  allowance += 0;
                } else {
                  //如果最后一张订单是返程订单
                  OrderEntity dayLastOrder = dayOrderArr.get(dayOrderArr.size() - 1);
                  if (dayLastOrder.getEndCity().equals(departure)) {
                    dayOrderArr.remove(dayOrderArr.size() - 1);
                  }
                  //当天所有城市中补贴最高的值
                  Set<String> cityNameSet = new HashSet<>();
                  if (CollectionUtils.isEmpty(dayOrderArr)) {
                    cityNameSet.add(dayLastOrder.getBeginCity());
                  } else {
                    for (OrderEntity dayOrder : dayOrderArr) {
                      cityNameSet.add(dayOrder.getEndCity());
                    }
                  }
                  allowance += getStandardByCitySet(cityNameSet);
                }
              } else {
                //如果当天没有订单则以之前最近的订单的目的标准作为补贴标准
                if (CollectionUtils.isEmpty(dayOrderArr)) {
                  LocalDate preDay = date.minusDays(1);
                  while (preDay.isAfter(firstLocalDate)) {
                    String dayKey = preDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                    dayOrderArr = dayOrderMap.get(dayKey);
                    if (!CollectionUtils.isEmpty(dayOrderArr)) {
                      break;
                    }
                    preDay = preDay.minusDays(1);
                  }
                  if (CollectionUtils.isEmpty(dayOrderArr)) {
                    allowance += 0;
                  } else {
                    //取最后一个行程的目的地
                    OrderEntity dayOrder = dayOrderArr.get(dayOrderArr.size() - 1);
                    allowance += getStandardByCity(dayOrder.getEndCity());
                  }
                } else {
                  //当天所有城市中补贴最高的值
                  Set<String> cityNameSet = new HashSet<>();
                  for (OrderEntity dayOrder : dayOrderArr) {
                    cityNameSet.add(dayOrder.getEndCity());
                  }
                  allowance += getStandardByCitySet(cityNameSet);
                }
              }
            }
          }
        }
      }
    }
    result.put("allowance", allowance);
    return result;
  }

  @Override
  public Map<String, Object> allowance(AllowanceRequest params) {
    Map<String, Object> result = new HashMap<>();
    //此阶段明细表尚无数据
    String type = params.getType();
    String travelApplyNo = params.getTravelApplyNo();
    int allowance = 0;
    result.put("applyNo", travelApplyNo);
    result.put("allowance", allowance);
    //合并火车和机票订单

    List<JourneyEntity> orderList = new ArrayList<>();
    orderList.addAll(params.getTrainOrders());
    orderList.addAll(params.getFlightOrders());
    if (CollectionUtils.isEmpty(orderList)) {
      return result;
    }
    orderList.sort(Comparator.comparing(JourneyEntity::getBeginTime));
    /**
     * 检查订单是否连续
     * 1、无订单或者订单数小于2
     * 2、上一段行程的目的地是下一段行程的出发地
     * 3、第一段行程的出发地等于最好一段行程的目的地
     */
    if (!checkContinuous(orderList)) {
      return result;
    } else {
      JourneyEntity firstOrder = orderList.get(0);
      JourneyEntity lastOrder = orderList.get(orderList.size() - 1);
      String beginTime = firstOrder.getBeginTime();
      String endTime = lastOrder.getEndTime();
      //获取两个时间内的小时数 确定天数
      LocalDateTime startDateTime = LocalDateTime.parse(beginTime, FORMATTER);
      LocalDateTime endDateTime = LocalDateTime.parse(endTime, FORMATTER);
      Duration duration = Duration.between(startDateTime, endDateTime);
      long hours = duration.toHours();
      //间隔天数
      int days = (int) Math.ceil(hours / 24.0);
      if ("1".equals(type)) {
        //内部会议出差
        allowance = 0;
      } else if ("2".equals(type)) {
        //外部会议培训
        allowance = 50 * days;
      } else {
        //订单按日分组
        Map<String, List<JourneyEntity>> dayOrderMap = new HashMap<>();
        for (int i = 0; i < orderList.size(); i++) {
          JourneyEntity order = orderList.get(i);
          String key = order.getBeginTime().substring(0, 10);
          if (dayOrderMap.containsKey(key)) {
            dayOrderMap.get(key).add(order);
          } else {
            List<JourneyEntity> arr = new ArrayList<>();
            arr.add(order);
            arr.sort(Comparator.comparing(JourneyEntity::getBeginTime));
            dayOrderMap.put(key, arr);
          }
        }
        Set<String> dateSet = dayOrderMap.keySet();
        List<String> dateList = dateSet.stream().sorted().collect(Collectors.toList());
        String preDate = dateList.get(0);
        LocalDateTime preLocalDate = LocalDate.parse(preDate, DateTimeFormatter
            .ofPattern("yyyy-MM-dd")).atStartOfDay();
        int preAllowance = calcAllowancePerDay(dayOrderMap.get(preDate), dayOrderMap.size()==1);
        allowance += preAllowance;
        List<Integer> allowanceList = Lists.newArrayList(preAllowance);
        for (int i = 1; i < dateList.size(); i++) {
          String curDate = dateList.get(i);
          //计算和上一个日期间隔的天数
          LocalDateTime curLocalDate = LocalDate.parse(curDate, DateTimeFormatter
              .ofPattern("yyyy-MM-dd")).atStartOfDay();
          int d = (int) Duration.between(preLocalDate, curLocalDate).toDays() - 1;
          allowance += (preAllowance * d);
          for (int j = 0; j < d; j++) {
            allowanceList.add(preAllowance);
          }
          int curAllowance = calcAllowancePerDay(dayOrderMap.get(curDate),
              i == dateList.size() - 1);
          allowance += curAllowance;
          allowanceList.add(curAllowance);
          preLocalDate = curLocalDate;
          preAllowance = curAllowance;
        }
        log.info("allowance per day:" + allowanceList.stream().map(String::valueOf)
            .collect(Collectors.joining(",")) + ", applyNo:" + travelApplyNo);
        //总的计算天数
        if (allowanceList.size() > days) {
          List<Integer> temp = new ArrayList<>(allowanceList);
          Collections.sort(temp);
          //按照差补值从小到大排序
          List<Integer> filter = temp.subList(0, temp.size() - days);
          log.info("allowance per day filter:" + filter.stream().map(String::valueOf)
              .collect(Collectors.joining(",")) + ", applyNo:" + travelApplyNo);
          //去掉对应天数最低的差补
          allowance -= filter.stream().reduce(Integer::sum).orElse(0);
        }
      }
    }
    result.put("allowance", allowance);
    return result;
  }

  private int calcAllowancePerDay(List<JourneyEntity> orderEntityList, boolean lastDay) {
    if (orderEntityList.size() < 2) {
      JourneyEntity orderEntity = orderEntityList.get(0);
      String city = lastDay ? orderEntity.getBeginCity() : orderEntity.getEndCity();
      return getStandardByCity(city);
    } else {
      Set<String> citySet = new HashSet<>();
      for (int i = 0; i < orderEntityList.size(); i++) {
        if (i == orderEntityList.size() - 1) {
          citySet.add(lastDay ? orderEntityList.get(i).getBeginCity()
              : orderEntityList.get(i).getEndCity());
        } else {
          citySet.add(orderEntityList.get(i).getEndCity());
        }
      }
      return getStandardByCitySet(citySet);
    }
  }

  private int getStandardByCitySet(Set<String> cityNameSet) {
    return cityNameSet.stream().map(this::getStandardByCity).max(Comparator.comparing(i -> i))
        .get();
  }

  private int getStandardByCity(String cityName) {
    if (StringUtils.isBlank(cityName)) {
      return 0;
    }
    return Lists.newArrayList("北京", "上海", "深圳", "广州", "海口", "三亚", "三沙").contains(cityName) ? 100
        : 80;
  }

  /**
   * 将日期区间拆分为每一天
   *
   * @param start
   * @param end
   * @return
   */
  private List<String> getDateList(String start, String end) {
    List<String> result = new ArrayList<>();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDate startDate = LocalDate.parse(start, formatter);
    LocalDate endDate = LocalDate.parse(end, formatter);
    LocalDate tempDate = startDate;
    while (tempDate.isBefore(endDate)) {
      result.add(tempDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
      tempDate = tempDate.plusDays(1);
    }
    result.add(endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    return result;
  }

  /**
   * 检查订单是否连续
   *
   * @param orderEntityList
   * @return
   */
  private boolean checkContinuous(List<JourneyEntity> orderEntityList) {
    boolean result = true;
    if (org.apache.commons.collections4.CollectionUtils.isEmpty(orderEntityList)
        || orderEntityList.size() < 2) {
      result = false;
    } else {
      JourneyEntity firstOrder = orderEntityList.get(0);
      JourneyEntity lastOrder = orderEntityList.get(orderEntityList.size() - 1);
      if (firstOrder.getBeginCity().equals(lastOrder.getEndCity())) {
        JourneyEntity pre = orderEntityList.get(0);
        for (int i = 1; i < orderEntityList.size(); i++) {
          JourneyEntity now = orderEntityList.get(i);
          if (!pre.getEndCity().equals(now.getBeginCity())) {
            result = false;
            break;
          }
          pre = now;
        }
      } else {
        result = false;
      }
    }

    return result;
  }

  public static void main(String[] args) {
    // GCONST.setRootPath("");
    // GCONST.setServerName("ecology");
    AllowanceServiceImpl allowanceService = new AllowanceServiceImpl();
    String json = "{\"type\":\"3\",\"isLeader\":\"false\",\"travelApplyNo\":\"20210821416\",\"journeyList\":[{\"beginTime\":\"2021-08-31 09:00:00\",\"endTime\":\"2021-09-01 17:00:00\",\"beginCity\":\"上海\",\"endCity\":\"南京\"}],\"trainOrders\":[{\"beginTime\":\"2021-09-01 09:20:00\",\"endTime\":\"2021-09-01 10:41:00\",\"beginCity\":\"上海\",\"endCity\":\"南京\"},{\"beginTime\":\"2021-09-01 16:32:00\",\"endTime\":\"2021-09-01 17:35:00\",\"beginCity\":\"南京\",\"endCity\":\"上海\"}],\"flightOrders\":[],\"orderList\":[]}";
    AllowanceRequest request = JSON.parseObject(json,
        AllowanceRequest.class);
    Map<String, Object> result = allowanceService.allowance(request);
    System.out.println(JSON.toJSONString(result));
  }
}
