package com.izouma.nineth.service;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.izouma.nineth.domain.Order;
import com.izouma.nineth.domain.User;
import com.izouma.nineth.enums.CollectionSource;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.OrderRepo;
import com.izouma.nineth.repo.TokenHistoryRepo;
import com.izouma.nineth.repo.UserRepo;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class StatisticService {

    private UserRepo         userRepo;
    private OrderRepo        orderRepo;
    private TokenHistoryRepo tokenHistoryRepo;

    public Map<String, Object> total(Long userId) {
        User user1 = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("无用户"));
        Map<String, Object> total = new HashMap<>();
        List<Order> orders;

        if (user1.isAdmin()) {
            long user = userRepo.countAllByDelFalse();
            total.put("userNum", user);
            orders = orderRepo.findAllByStatus(OrderStatus.FINISH);
        } else {
            orders = orderRepo.findAllByStatusAndMinterId(OrderStatus.FINISH, userId);
        }

        Map<CollectionSource, List<Order>> orderMap = orders.stream().collect(Collectors.groupingBy(Order::getSource));
        List<Order> officialOrders = orderMap.get(CollectionSource.OFFICIAL);
        List<Order> transferOrders = orderMap.get(CollectionSource.TRANSFER);
        if (officialOrders == null) {
            total.put("officialNum", 0);
            total.put("transferNum", 0);
            total.put("officialPrice", 0);
            total.put("transferPrice", 0);
            return total;
        }

        total.put("officialNum", officialOrders.size());
        total.put("transferNum", transferOrders.size());

        BigDecimal officialPrice = officialOrders.stream()
                .map(Order::getTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal transferPrice = transferOrders.stream()
                .map(Order::getTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        total.put("officialPrice", officialPrice);
        total.put("transferPrice", transferPrice);

        return total;
    }

    /*
    按天
     */
    public Map<String, Long> userTrend(int day) {
        Map<String, Long> map = new HashMap<>();
        for (int i = 0; i < day; i++) {
            LocalDate date = LocalDate.now().minusDays(i);
            map.put(DateTimeFormatter.ofPattern("yyyy-MM-dd").format(date),
                    userRepo.countByCreatedAtBetween(date.atStartOfDay(), date.atTime(LocalTime.MAX)));
        }
        return map;
    }

    /*
    按月
     */
    public Map<String, Long> userTrendV2(String yearMonth) {
        Map<String, Long> map = new HashMap<>();
        YearMonth month = YearMonth.parse(yearMonth);
        LocalDateTime end = month.atEndOfMonth().atTime(LocalTime.MAX);
        LocalDateTime start = month.atDay(1).atStartOfDay();
        map.put(yearMonth, userRepo.countByCreatedAtBetween(start, end));
        return map;
    }

    public Map<String, Map<String, Long>> orderNumTrend(int day, Long userId) {
        LocalDateTime date = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        LocalDateTime start = date.minusDays(day);

        User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("无用户"));
        List<Order> orders;
        if (user.isAdmin()) {
            orders = orderRepo.findAllByCreatedAtIsAfterAndStatusIn(start,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        } else {
            orders = orderRepo.findAllByCreatedAtIsAfterAndMinterIdAndStatusIn(start, userId,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        }


        Map<CollectionSource, List<Order>> orderMap = orders.stream().collect(Collectors.groupingBy(Order::getSource));

        Map<String, Long> official = null;
        if (CollUtil.isNotEmpty(orderMap.get(CollectionSource.OFFICIAL))) {
            official = orderMap.get(CollectionSource.OFFICIAL).stream().collect(Collectors.groupingBy(
                    item -> DateTimeFormatter.ofPattern("yyyy-MM-dd")
                            .format(item.getCreatedAt()), Collectors.counting()));
        }

        Map<String, Long> transfer = null;
        if (CollUtil.isNotEmpty(orderMap.get(CollectionSource.TRANSFER))) {
            transfer = orderMap.get(CollectionSource.TRANSFER).stream().collect(Collectors.groupingBy(
                    item -> DateTimeFormatter.ofPattern("yyyy-MM-dd")
                            .format(item.getCreatedAt()), Collectors.counting()));
        }

        Map<String, Map<String, Long>> trend = new HashMap<>();
        trend.put("official", official);
        trend.put("transfer", transfer);
        return trend;
    }

    public Map<String, Map<String, BigDecimal>> orderPriceTrend(int day, Long userId) {
        LocalDateTime date = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        LocalDateTime start = date.minusDays(day);

        User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("无用户"));
        List<Order> orders;
        if (user.isAdmin()) {
            orders = orderRepo.findAllByCreatedAtIsAfterAndStatusIn(start,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        } else {
            orders = orderRepo.findAllByCreatedAtIsAfterAndMinterIdAndStatusIn(start, userId,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        }

        Map<CollectionSource, List<Order>> orderMap = orders.stream().collect(Collectors.groupingBy(Order::getSource));
        Map<String, BigDecimal> official = null;
        if (CollUtil.isNotEmpty(orderMap.get(CollectionSource.OFFICIAL))) {
            official = orderMap.get(CollectionSource.OFFICIAL)
                    .stream()
                    .collect(Collectors.groupingBy(
                            item -> DateTimeFormatter.ofPattern("yyyy-MM-dd")
                                    .format(item.getCreatedAt()), Collectors.mapping(Order::getTotalPrice,
                                    Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
        }

        Map<String, BigDecimal> transfer = null;
        if (CollUtil.isNotEmpty(orderMap.get(CollectionSource.TRANSFER))) {
            transfer = orderMap.get(CollectionSource.TRANSFER)
                    .stream()
                    .collect(Collectors.groupingBy(
                            item -> DateTimeFormatter.ofPattern("yyyy-MM-dd")
                                    .format(item.getCreatedAt()), Collectors.mapping(Order::getTotalPrice,
                                    Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
        }

        Map<String, Map<String, BigDecimal>> trend = new HashMap<>();
        trend.put("official", official);
        trend.put("transfer", transfer);
        return trend;
    }

    public String top(LocalDateTime start, LocalDateTime end, int size) {
        List<Map<String, Object>> maps = tokenHistoryRepo.sumPrice(start, end, size);
        return JSONObject.toJSONString(maps);
    }

    public Map<String, BigDecimal> orderPriceTrendV2(String yearMonth, Long userId) {
        YearMonth month = YearMonth.parse(yearMonth);
        LocalDateTime end = month.atEndOfMonth().atTime(LocalTime.MAX);
        LocalDateTime start = month.atDay(1).atStartOfDay();

        User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("无用户"));
        List<Order> orders;
        if (user.isAdmin()) {
            orders = orderRepo.findAllByCreatedAtBetweenAndStatusIn(start, end,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        } else {
            orders = orderRepo.findAllByCreatedAtBetweenAndMinterIdAndStatusIn(start, end, userId,
                    Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
        }
        Map<String, BigDecimal> map = new HashMap<>();
        map.put("official", orders.stream().filter(order -> order.getSource().equals(CollectionSource.OFFICIAL))
                .map(Order::getPrice).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
        map.put("transfer", orders.stream().filter(order -> order.getSource().equals(CollectionSource.TRANSFER))
                .map(Order::getPrice).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
        return map;
    }
}
