package com.izouma.dingdong.service.merchant;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.izouma.dingdong.converter.LongArrayConverter;
import com.izouma.dingdong.domain.MoneyRecord;
import com.izouma.dingdong.domain.OrderInfo;
import com.izouma.dingdong.domain.User;
import com.izouma.dingdong.domain.backstage.*;
import com.izouma.dingdong.domain.merchant.Goods;
import com.izouma.dingdong.domain.merchant.Merchant;
import com.izouma.dingdong.domain.merchant.MerchantSettings;
import com.izouma.dingdong.domain.merchant.Sales;
import com.izouma.dingdong.domain.user.UserCoupon;
import com.izouma.dingdong.dto.FilterDTO;
import com.izouma.dingdong.dto.MerchantDTO;
import com.izouma.dingdong.dto.PageQuery;
import com.izouma.dingdong.enums.ApplyStatus;
import com.izouma.dingdong.enums.FinancialType;
import com.izouma.dingdong.exception.BusinessException;
import com.izouma.dingdong.repo.*;
import com.izouma.dingdong.repo.backstage.*;
import com.izouma.dingdong.repo.merchant.*;
import com.izouma.dingdong.repo.user.UserCouponRepo;
import com.izouma.dingdong.service.backstage.CategoryService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class MerchantSettingsService {

    private MerchantSettingsRepo       merchantSettingsRepo;
    private OrderInfoRepo              orderInfoRepo;
    private TimeTagRepo                timeTagRepo;
    private MerchantRepo               merchantRepo;
    private UserRepo                   userRepo;
    private MoneyRecordRepo            moneyRecordRepo;
    private BannerRepo                 bannerRepo;
    private CategoryRepo               categoryRepo;
    private CategoryService            categoryService;
    private PromoteRepo                promoteRepo;
    private GoodsRepo                  goodsRepo;
    private AppraisalRepo              appraisalRepo;
    private GoodsSpecificationRepo     goodsSpecificationRepo;
    private MerchantClassificationRepo merchantClassificationRepo;
    private FullReductionRepo          fullReductionRepo;
    private MerchantService            merchantService;
    private CouponRepo                 couponRepo;
    private UserCouponRepo             userCouponRepo;
    private PriorityRepo               priorityRepo;
    private SalesRepo                  salesRepo;

    /*
    时间标签类商家列表
     */
    public List<MerchantDTO> timeMerchant(Long timeTagId) {
        TimeTag timeTag = timeTagRepo.findById(timeTagId).orElseThrow(new BusinessException("无时间标签"));
        String dayOfWeek = LocalDateTime.now().getDayOfWeek().toString();

        List<MerchantSettings> all = merchantSettingsRepo.findAll();
        List<MerchantSettings> collect = all.stream().filter(a ->
                //商家开始时间早于结束前的一小时 或者 商家结束时间早于开始后的一小时 且商家本日营业
                (!a.getStartTime().isAfter(timeTag.getEndTime().minusHours(1)) || !a.getEndTime()
                        .isAfter(timeTag.getStartTime().plusHours(1))) && a.getIsOpening() && a.getWeek()
                        .contains(dayOfWeek)
        ).collect(Collectors.toList());

        List<MerchantDTO> list = CollUtil.newArrayList();

        collect.forEach(a -> {
            Merchant merchant = merchantRepo.findById(a.getMerchantId()).orElseThrow(new BusinessException("无商家"));
            list.add(new MerchantDTO(merchant, a));
        });

        return list;
    }


    /**
     * 商户叮咚币记录
     *
     * @param merchantId 商户id
     * @param fromUserId 支出用户
     * @param amount     金额
     * @param type       类型
     * @param remark     备注
     */
    public void income(Long merchantId, Long fromUserId, BigDecimal amount, FinancialType type, String remark) {
        //按商户Id找出用户
        User userMer = userRepo.findById(merchantRepo.findUserIdById(merchantId))
                .orElseThrow(new BusinessException("无用户"));
        //商家应得 = 减去骑手应得，减去平台抽成
        userMer.setMoney(userMer.getMoney().add(amount));
        userRepo.save(userMer);

        //购买的user
        User user = userRepo.findById(fromUserId).orElseThrow(new BusinessException("无用户"));

        //商家记录到对账单
        moneyRecordRepo.save(
                MoneyRecord.builder()
                        .avatar(user.getAvatar())
                        .name(user.getNickname())
                        .type(type)
                        .time(LocalDateTime.now())
                        .amount(amount)
                        .userId(userMer.getId())
                        .fromUserId(fromUserId)
                        .enabled(true)
                        .remark(remark)
                        .build()
        );
    }

    /*
     * 首页上的东西
     * banner
     * 分类
     * 优惠专区
     * 每日力荐
     * 新店推荐
     * 推荐商家
     * 发现美食
     */
    public Map<String, Object> index(Double longitude, Double latitude) {
        //banner
        List<Banner> banners = bannerRepo.findAll();

        //一级分类
        List<Category> categories = categoryRepo.findAllByParent((long) 1);

        //时间标签
        List<TimeTag> timeTags = timeTagRepo.findAll();
        LocalTime now = LocalTime.now();
        List<TimeTag> timeTag = timeTags.stream()
                .filter(t -> now.isAfter(t.getStartTime()) && now.isBefore(t.getEndTime()))
                .collect(Collectors.toList());

        //优惠专区
        //主推荐位 2个
        List<Promote> promote1 = promoteRepo.findAllByAction(1);
        if (promote1.size() > 2) {
            promote1.subList(0, 2);
        }
        //副推荐位 4个
        List<Promote> promote2 = promoteRepo.findAllByAction(2);
        if (promote2.size() > 4) {
            promote2.subList(0, 4);
        }

        //新店推荐 最多8个
        LocalDate date = LocalDate.now().minusMonths(1);
        LocalTime time = LocalTime.parse("00:00:00");
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        List<Merchant> newMerchants = merchantRepo.findAllByEstablishTimeAfter(dateTime);
        if (newMerchants.size() > 8) {
            newMerchants.subList(0, 8);
        }


        Map<String, Object> map = new HashMap<>();
        map.put("banner", banners);
        map.put("categories", categories);
        map.put("timeTag", timeTag.get(0));
        map.put("promote1", promote1);
        map.put("promote2", promote2);
        map.put("newMerchants", newMerchants);
        return map;
    }


    /*
    人均消费 月订单
     */
    public BigDecimal consumption(Long merchantId) {
        List<OrderInfo> orders = orderInfoRepo.findAllByMerchantId(merchantId);
        List<BigDecimal> collect = orders.stream().map(o ->
                o.getRealAmount().subtract(o.getDeliveryAmount())
        ).collect(Collectors.toList());

        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal c : collect) {
            total = total.add(c);
        }

        return total.divide(BigDecimal.valueOf(collect.size()), 2);
    }


    /*
    删除商家
     */
    public void del(Long merchantId) {
        //删除商户
        merchantRepo.deleteById(merchantId);
        merchantSettingsRepo.deleteByMerchantId(merchantId);
        //删除商品规格
        List<Long> collect = goodsRepo.findAllByMerchantId(merchantId)
                .stream()
                .map(Goods::getId)
                .collect(Collectors.toList());
        collect.forEach(c -> goodsSpecificationRepo.deleteByGoodsId(c));
        //删除商品
        goodsRepo.deleteAllByMerchantId(merchantId);
        //删除订单
        orderInfoRepo.deleteAllByMerchantId(merchantId);
        //删除评价
        appraisalRepo.deleteAllByMerchantId(merchantId);
        //删除商家分类
        merchantClassificationRepo.findAllByMerchantId(merchantId);

    }

    public MerchantDTO getDTO(Long id) {
        Merchant merchant = merchantRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        MerchantSettings merchantSettings = merchantSettingsRepo.findByMerchantId(id)
                .orElseThrow(new BusinessException("无记录"));
        MerchantDTO dto = new MerchantDTO(merchant, merchantSettings);
        dto.setFullReductions(fullReductionRepo.findAllByMerchantId(id));
        return dto;
    }

    /*
    //好评优先 1
    //起送费最低 2
    //配送最快 3
    //配送费最低 4
    //人均最低 5
    //人均最高 6
    //距离 7
    //销量 8
    */
    //排序
    public List<MerchantDTO> listSort(Double longitude, Double latitude, Integer sort) {
        //处于营业内
        LocalTime now = LocalTime.now();
        String dayOfWeek = LocalDateTime.now().getDayOfWeek().toString();
        List<MerchantSettings> all = merchantSettingsRepo.findAll();

        List<MerchantSettings> collect = all.stream()
                .filter(a -> a.getStartTime().isBefore(now) && a.getEndTime().isAfter(now) && a.getPriority() >= 2
                        && a.getIsOpening() && a.getWeek()
                        .contains(dayOfWeek)
                )
                .collect(Collectors.toList());
        Set<Merchant> merchantList = new HashSet<>();

        collect.forEach(c ->
                merchantRepo.findById(c.getMerchantId()).ifPresent(merchantList::add)
        );
        //带距离 且按距离排好序
        List<MerchantDTO> dtos = merchantService.withDistance(merchantList, longitude, latitude, null);

        if (ObjectUtil.isNotNull(sort)) {
            switch (sort) {
                case 1:
                    //好评
                    dtos.sort((a, b) -> {
                        Integer sumA = a.getGoodNum() - a.getBadNum() * 10;
                        Integer sumB = b.getGoodNum() - b.getBadNum() * 10;
                    /*if (i == 0) {
                        return a.getDistance().compareTo(b.getDistance());
                    }*/
                        return sumA.compareTo(sumB);
                    });
                    break;
                case 2:
                    //起送价
                    dtos.sort(Comparator.comparing(MerchantDTO::getStartingAmount));
                    break;
                case 3:
                    //平均配送时间 暂时 准备时间
                    dtos.sort(Comparator.comparing(MerchantDTO::getPreparationTime));
                    break;
                case 4:
                    //配送费最低？？
                case 5:
                    //人均最低
                    dtos.sort((a, b) -> {
                        BigDecimal conA = this.consumption(a.getMid());
                        BigDecimal conB = this.consumption(b.getMid());
                        return conA.compareTo(conB);
                    });
                    break;
                case 6:
                    //人均最高
                    dtos.sort((a, b) -> {
                        BigDecimal conA = this.consumption(a.getMid());
                        BigDecimal conB = this.consumption(b.getMid());

                        return conB.compareTo(conA);
                    });
                    break;
                //case 7:
                //距离
                //dtos.sort(Comparator.comparing(MerchantDTO::getDistance));
                case 8:
                    //月销
                    dtos.sort((a, b) -> b.getMonthSales().compareTo(a.getMonthSales()));
                    break;
                default:
                    break;
            }
        }
        return dtos;
    }

    public List<MerchantDTO> listFilter(List<MerchantDTO> dtos, String filter, Long userId) {
        Set<MerchantDTO> collect = new HashSet<>();
        LongArrayConverter converter = new LongArrayConverter();
        List<Long> longs = converter.convertToEntityAttribute(filter);
        for (Long l : longs) {
            if (l == 1) {//首单立减
                collect.addAll(dtos.stream()
                        .filter(d -> d.getFirstOrder() != null && d.getFirstOrder().compareTo(BigDecimal.ZERO) > 0)
                        .collect(Collectors.toSet()));
            } else if (l == 2) {//满减优惠
                collect.addAll(dtos.stream()
                        .filter(d -> CollUtil.isNotEmpty(fullReductionRepo.findAllByMerchantId(d.getMid())))
                        .collect(Collectors.toSet()));
            } else if (l == 3) {//折扣商家
                collect.addAll(dtos.stream()
                        .filter(d -> StrUtil.isNotEmpty(
                                merchantClassificationRepo.findByMerchantIdAndType(d.getMid(), 2).getGoodsIds()))
                        .collect(Collectors.toSet()));
            } else if (l == 4) {//下单返红包
                collect.addAll(dtos.stream()
                        .filter(d -> CollUtil.isNotEmpty(couponRepo.findAllByMerchantIdAndEnabledTrue(d.getMid())))
                        .collect(Collectors.toSet()));
            } else if (l == 5) {//可用红包
                List<UserCoupon> coupons = userCouponRepo.findAllByUserIdAndIsUsedFalse(userId);
                collect.addAll(dtos.stream()
                        .filter(d ->
                                ObjectUtil.isNotEmpty(coupons.stream().filter(c ->
                                        !c.getCoupon().getEndDate().isBefore(LocalDate.now())
                                                && (c.getCoupon().getMerchantId() == null || c.getCoupon()
                                                .getMerchantId()
                                                .equals(d.getMid()))
                                ).collect(Collectors.toSet()))
                        )
                        .collect(Collectors.toSet()));
            }
        }
        return new ArrayList<>(collect);
    }

    //其他筛选条件
    public List<MerchantDTO> otherFilter(List<MerchantDTO> dtos, FilterDTO filterDto) {
        Set<MerchantDTO> collect = new HashSet<>();
        LongArrayConverter converter = new LongArrayConverter();
        if (filterDto.getNatureId() != null) {
            List<Long> natureIds = converter.convertToEntityAttribute(filterDto.getFilter());
            //商家性质筛选
            natureIds.forEach(n ->
                    collect.addAll(dtos.stream()
                            .filter(d -> d.getMerchantNatureId().equals(n))
                            .collect(Collectors.toSet()))
            );
        }

        //金额区间
        if (filterDto.getStartAmount() != null && filterDto.getEndAmount() != null) {
            collect.addAll(dtos.stream()
                    .filter(d -> this.consumption(d.getMid()).compareTo(filterDto.getStartAmount()) >= 0
                            && this.consumption(d.getMid()).compareTo(filterDto.getEndAmount()) <= 0
                    ).collect(Collectors.toSet()));

        } else if (filterDto.getStartAmount() != null) {
            collect.addAll(dtos.stream()
                    .filter(d -> this.consumption(d.getMid()).compareTo(filterDto.getStartAmount()) >= 0
                    ).collect(Collectors.toSet()));

        } else if (filterDto.getEndAmount() != null) {
            collect.addAll(dtos.stream()
                    .filter(d -> this.consumption(d.getMid()).compareTo(filterDto.getEndAmount()) <= 0
                    ).collect(Collectors.toSet()));
        } else {
            collect.addAll(dtos);
        }

        //标签
        if (filterDto.getTag() != null) {
            List<Long> tags = converter.convertToEntityAttribute(filterDto.getTag());
            tags.forEach(t -> {
                if (t == 1) {//新商家
                    collect.addAll(dtos.stream()
                            .filter(d -> d.getTag() == 1)
                            .collect(Collectors.toSet()));
                } else if (t == 2) {//明星商家
                    collect.addAll(dtos.stream()
                            .filter(d -> d.getTag() == 2)
                            .collect(Collectors.toSet()));
                } else if (t == 3) {//口碑联盟
                    //口碑联盟
                    collect.addAll(dtos.stream()
                            .filter(MerchantDTO::getBuyAlliance)
                            .collect(Collectors.toSet()));
                }

            });
        }
        return new ArrayList<>(collect);
    }

    /*
    推荐商家
     */
    public List<MerchantDTO> recommended(Double longitude, Double latitude, Integer sort, FilterDTO filterDto, Long userId) {
        List<MerchantDTO> dtoList;
        dtoList = this.listSort(longitude, latitude, sort);
        if (filterDto.getFilter() != null) {
            dtoList = this.listFilter(dtoList, filterDto.getFilter(), userId);
        }
        return this.otherFilter(dtoList, filterDto);
    }


    public List<MerchantDTO> showAll(PageQuery pageQuery, Double longitude, Double latitude, Long popularTag, Long userId) {

        List<Merchant> merchantList = merchantRepo.findAll((root, criteriaQuery, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();
                    predicates.add(criteriaBuilder.equal(root.get("status"), ApplyStatus.PASS));
                    if (StrUtil.isNotBlank(pageQuery.getSearch())) {
                        predicates.add(criteriaBuilder.like(root.get("name"), "%" + pageQuery.getSearch() + "%"));
                    }
                    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
                }
        );

        Set<Merchant> merchants = new HashSet<>(merchantList);

        if (StrUtil.isNotBlank(pageQuery.getSearch())) {
            List<Goods> goods = goodsRepo.findAll((root, criteriaQuery, criteriaBuilder) ->
                    criteriaQuery.where(
                            criteriaBuilder.and(
                                    criteriaBuilder.like(root.get("name"), "%" + pageQuery.getSearch() + "%")),
                            criteriaBuilder.equal(root.get("status"), ApplyStatus.PASS)).getRestriction()
            );

            goods.forEach(g -> {
                Merchant merchant = merchantRepo.findById(g.getMerchantId()).orElse(null);
                if (ObjectUtil.isNotNull(merchant) && ApplyStatus.PASS.equals(merchant.getStatus())) {
                    merchants.add(merchant);
                }
            });
        }

        //带距离 且按距离排好序
        List<MerchantDTO> dtos = merchantService.withDistance(merchants, longitude, latitude, null);
        if (popularTag != null) {
            return this.listFilter(dtos, popularTag.toString(), userId);
        }
        return dtos;
    }

    //月更的东西
    /*
    推广语
    优先级
    明星商家
     */
    public void monthUpdate() {
        List<Merchant> merchants = merchantRepo.findAll();
        merchants.forEach(a -> {
            //明星商家
            this.starMerchant(a);
            //优先级
            this.priority(a.getId(), 0);
        });
        List<Goods> goods = goodsRepo.findAll();
        //优先级
        goods.forEach(g -> this.priority(g.getId(), 1));
        this.merPromo();
    }

    /*
    明星商家
     */
    public void starMerchant(Merchant merchant) {
        LocalDate date = LocalDate.now().minusMonths(1);
        LocalTime time = LocalTime.parse("00:00:00");
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        //非新商家
        boolean b = dateTime.isAfter(merchant.getEstablishTime());
        if (b) {
            if (merchant.getMonthSales() >= 10000 && merchant.getBadNum() > 0 && merchant.getGoodNum() > 3000) {
                //明星商家
                merchant.setTag(2);
            } else {
                //都不是
                merchant.setTag(0);
            }
        } else {
            //新商家
            merchant.setTag(1);
        }
        merchantRepo.save(merchant);
    }

    //商家推广语
    //每月一更
    public void merPromo() {
        List<Category> byParent = categoryRepo.findAllByParent(1L);
        byParent.forEach(this::accept);
    }

    private void accept(Category c) {
        List<MerchantDTO> list = categoryService.categoryMer(c.getId());
        List<Merchant> merchants = new ArrayList<>();
        list.sort((a, b) -> b.getMonthSales().compareTo(a.getMonthSales()));

        for (int i = 0; i < 5; i++) {
            Merchant merchant = merchantRepo.findById(list.get(i).getMid()).orElseThrow(new BusinessException("无商家"));
            String name;
            switch (i) {
                case 0:
                    name = "商家月销售冠军";
                    break;
                case 1:
                    name = "商家月销售亚军";
                    break;
                case 2:
                    name = "商家月销售季军";
                    break;
                case 3:
                    name = "商家月销售第四名";
                    break;
                default:
                    name = "商家月销售第五名";
            }
            merchant.setPromo(c.getName() + name);
            merchants.add(merchant);
        }
        merchantRepo.saveAll(merchants);
    }

    //更新商品/商户优先级,每月1日更新
    public void priority(Long id, Integer type) {

        LocalDate now = LocalDate.now();
        //上个月日期
        LocalDate lastMonth = now.minusMonths(1);

        LocalDate day = now.minusDays(1);
        //月销为0天数
        int countZeroSales = 0;
        //点赞数
        Integer likes = 0;
        //差评数
        Integer badReview = 0;
        //销量
        Integer sales = 0;

        while (day.isBefore(lastMonth)) {
            Sales daySales;
            if (type == 0) {
                daySales = salesRepo.findByMerchantIdAndDay(id, day);
            } else {
                daySales = salesRepo.findByGoodsIdAndDay(id, day);
            }

            if (ObjectUtil.isNull(daySales)) {
                //月销为0天数
                countZeroSales++;
            } else {
                likes += daySales.getDayLikes();
                badReview += daySales.getDayBad();
                sales += daySales.getDaySales();
            }
            day = now.minusDays(1);
        }

        boolean add = true;
        boolean plus = false;

        //上一月该商品新增点赞数-新增差评数*10>0
        if (likes - badReview * 10 < 0) {
            add = false;
        }

        Integer p;
        Priority priority;

        MerchantSettings settings = null;
        Goods goods = null;
        if (type == 0) {
            settings = merchantSettingsRepo.findByMerchantId(id).orElseThrow(new BusinessException("无商户"));

            p = settings.getPriority();
            priority = priorityRepo.findByTypeAndLevel(0, p);
        } else {
            goods = goodsRepo.findById(id).orElseThrow(new BusinessException("无商品"));

            p = goods.getPriority();
            priority = priorityRepo.findByTypeAndLevel(1, p);
        }


        //新增点赞数  月销量
        if (likes < priority.getLikes() || sales < priority.getMonthSales()) {
            add = false;
        }

        if (countZeroSales < priority.getZeroSalesDay()) {
            plus = true;
        } else if (likes - badReview * 3 < 0) {
            plus = true;
        } else if (sales < priority.getLessMonthSales()) {
            plus = true;
        }

        if (add) {
            if (!p.equals(priorityRepo.findByTypeAndLevelMax(1))) {
                if (type == 0) {
                    settings.setPriority(p + 1);
                    merchantSettingsRepo.save(settings);
                } else {
                    goods.setPriority(p + 1);
                    goodsRepo.save(goods);
                }
            }
        }
        if (plus) {
            if (!p.equals(1)) {
                if (type == 0) {
                    settings.setPriority(p - 1);
                    merchantSettingsRepo.save(settings);
                } else {
                    goods.setPriority(p - 1);
                    goodsRepo.save(goods);
                }
            }
        }
    }
}
