package com.izouma.dingdong.service.merchant;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.izouma.dingdong.config.Constants;
import com.izouma.dingdong.converter.LongArrayConverter;
import com.izouma.dingdong.domain.User;
import com.izouma.dingdong.domain.Verified;
import com.izouma.dingdong.domain.backstage.Category;
import com.izouma.dingdong.domain.merchant.Goods;
import com.izouma.dingdong.domain.merchant.Merchant;
import com.izouma.dingdong.domain.merchant.MerchantClassification;
import com.izouma.dingdong.domain.merchant.MerchantSettings;
import com.izouma.dingdong.domain.user.UserCoupon;
import com.izouma.dingdong.dto.MerchantDTO;
import com.izouma.dingdong.dto.PageQuery;
import com.izouma.dingdong.enums.ApplyStatus;
import com.izouma.dingdong.exception.BusinessException;
import com.izouma.dingdong.repo.CouponRepo;
import com.izouma.dingdong.repo.UserRepo;
import com.izouma.dingdong.repo.VerifiedRepo;
import com.izouma.dingdong.repo.backstage.CategoryRepo;
import com.izouma.dingdong.repo.merchant.*;
import com.izouma.dingdong.repo.user.UserCouponRepo;
import com.izouma.dingdong.utils.MapUtils;
import com.izouma.dingdong.utils.ObjUtils;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;

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

import static com.izouma.dingdong.web.BaseController.*;

@Service
@AllArgsConstructor
public class MerchantService {

    private MerchantRepo               merchantRepo;
    private MerchantSettingsRepo       merchantSettingsRepo;
    private UserRepo                   userRepo;
    private MerchantClassificationRepo merchantClassificationRepo;
    private VerifiedRepo               verifiedRepo;
    private FullReductionRepo          fullReductionRepo;
    private CouponRepo                 couponRepo;
    private UserCouponRepo             userCouponRepo;
    private CategoryRepo               categoryRepo;
    private GoodsRepo                  goodsRepo;

    /*
    商户注册申请
     */
    public MerchantDTO registerApply(MerchantDTO merchantDTO) {
        //注册商家用户
        // User user = userService.register(merchantDTO.getPhone(), merchantDTO.getPassword());
        User user = userRepo.findByPhone(merchantDTO.getPhone());

        //查看商家是否已存在
        Merchant merchant1 = merchantRepo.findByPhone(merchantDTO.getPhone());

        if (ObjectUtil.isNotNull(merchant1)) {
            if (merchant1.getStatus().equals(ApplyStatus.PENDING)) {
                //return merchantDTO;
                throw new BusinessException("申请中");
            }
            if (merchant1.getStatus().equals(ApplyStatus.PASS)) {
                //return merchantDTO;
                throw new BusinessException("该手机号已申请");
            }
        }

        //新建商家
        Merchant merchant = new Merchant();
        MerchantSettings merchantSettings = new MerchantSettings();

        //dto转merchant实体
        BeanUtil.copyProperties(merchantDTO, merchant);
        merchant.setUserId(user.getId());
        merchant.setStatus(ApplyStatus.PENDING);

        merchant.setMonthSales(0);
        merchant.setGoodNum(0);
        merchant.setBadNum(0);

        merchant.setEnabled(true);
        merchant.setBlacklist(false);
        merchant = merchantRepo.save(merchant);

        //dto转merchantSettings实体
        BeanUtil.copyProperties(merchantDTO, merchantSettings);
        merchantSettings.setMerchantId(merchant.getId());
        //默认不自动接单
        merchantSettings.setAutomaticOrder(false);

        //新用户和首单金额未设置时
/*        if (ObjectUtil.isNull(merchantDTO.getNewUser())) {
            merchantSettings.setNewUser(BigDecimal.ZERO);
        }*/
        if (ObjectUtil.isNull(merchantDTO.getFirstOrder())) {
            merchantSettings.setFirstOrder(BigDecimal.ZERO);
        }

        merchantSettings.setEnabled(true);
        merchantSettings.setIsOpening(true);

        //优先级
        merchantSettings.setPriority(1);
        //购买口碑联盟
        merchantSettings.setBuyAlliance(false);
        merchantSettingsRepo.save(merchantSettings);

        //新建 系统预制分类：好评热销/折扣/点单必读
        merchantClassificationRepo.save(MerchantClassification.builder()
                .merchantId(merchant.getId())
                .name(Constants.CLASSIFICATION_NAME)
                .sort(2)
                .isOpen(false)
                .isShow(true)
                .type(1)
                .enabled(true)
                .build());
        merchantClassificationRepo.save(MerchantClassification.builder()
                .merchantId(merchant.getId())
                .name(Constants.CLASSIFICATION_NAME2)
                .sort(3)
                .isOpen(true)
                .isShow(false)
                .type(2)
                .enabled(true)
                .build());
        merchantClassificationRepo.save(MerchantClassification.builder()
                .merchantId(merchant.getId())
                .name(Constants.CLASSIFICATION_NAME3)
                .sort(1)
                .isOpen(true)
                .isShow(false)
                .type(3)
                .enabled(true)
                .build());

        return merchantDTO;
    }

    /**
     * 商户修改信息
     *
     * @param merchantDTO 修改的信息
     * @return 修改
     */
    public MerchantDTO change(MerchantDTO merchantDTO) {
        if (merchantDTO.getCategoryId() != null) {
            Set<Category> categories = new HashSet<>();
            LongArrayConverter converter = new LongArrayConverter();
            List<Long> longs = converter.convertToEntityAttribute(merchantDTO.getCategoryId());
            longs.forEach(l -> {
                Category category = categoryRepo.findById(l).orElseThrow(new BusinessException("无分类"));
                categories.add(category);
            });
            merchantDTO.setCategory(categories);
        }

        if (merchantDTO.getMid() == null) {
            return registerApply(merchantDTO);
        }

        Merchant merchant = merchantRepo.findById(merchantDTO.getMid()).orElseThrow(new BusinessException("商户不存在"));
        MerchantSettings merchantSettings = merchantSettingsRepo.findByMerchantId(merchantDTO.getMid())
                .orElseThrow(new BusinessException("商户不存在"));

        MerchantDTO dto = new MerchantDTO(merchant, merchantSettings);
        ObjUtils.merge(dto, merchantDTO);

        User user = merchant.getUser();
        user.setAvatar(dto.getLogo());
        user.setPhone(dto.getPhone());
        user.setNickname(dto.getShowName());
        userRepo.save(user);

        //dto转实体
        BeanUtil.copyProperties(dto, merchant);
        BeanUtil.copyProperties(dto, merchantSettings);
        merchantRepo.save(merchant);
        merchantSettingsRepo.save(merchantSettings);

        return dto;
    }

    /**
     * 商家过审
     *
     * @param merchantId 商户ID
     * @param pass       是否过审
     */
    public void audit(Long merchantId, Boolean pass, String promo) {
        Merchant merchant = merchantRepo.findById(merchantId).orElseThrow(new BusinessException("商户不存在"));

        if (pass) {
            merchant.setStatus(ApplyStatus.PASS);
            merchant.setIsPass(true);
            merchant.setEstablishTime(LocalDateTime.now());
            //标签 新商家
            merchant.setTag(1);
            //推广语
            merchant.setPromo(promo);

            //审核通过后无法修改实名信息
            Verified verified = verifiedRepo.findByUserId(merchant.getUserId());
            if (verified == null) {
                throw new BusinessException("无法人信息");
            }
            verified.setCanEdit(false);
            verifiedRepo.save(verified);

        } else {
            merchant.setStatus(ApplyStatus.DENY);
            merchant.setIsPass(false);
        }
        merchantRepo.save(merchant);

    }

    /**
     * 管理后台显示所有商家信息
     * 按时间排序
     *
     * @param pageQuery 分页
     * @return 分页列表
     */
    public Page<MerchantDTO> backAll(PageQuery pageQuery) {

        List<MerchantDTO> merchantDTOS = CollUtil.newArrayList();
        Page<Merchant> merchants = merchantRepo.findAll(toSpecification(pageQuery, Merchant.class), toPageRequest(pageQuery));
        for (Merchant merchant : merchants) {
            MerchantSettings merchantSettings = merchantSettingsRepo.findByMerchantId(merchant.getId())
                    .orElseThrow(new BusinessException("商户不存在"));

            merchantDTOS.add(new MerchantDTO(merchant, merchantSettings));
        }
        return new PageImpl<>(merchantDTOS, toPageRequest(pageQuery), merchantDTOS.size());
    }

    /**
     * 用户端显示的所有商家
     *
     * @param pageQuery  分页
     * @param longitude  经度
     * @param latitude   纬度
     * @param popularTag 热门标签
     * @param userId     用户Id
     * @return 用户附近的上级列表
     */
    public List<MerchantDTO> showAll(PageQuery pageQuery, Double longitude, Double latitude, String 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]));
                }
                //criteriaBuilder.like(root.get("name"), "%" + pageQuery.getSearch() + "%")).getRestriction()
        );

        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);
                }
            });
        }
/*        //所有商家要按距离排序规则
        Map<Merchant, Double> map = new HashMap<>();
        //算距离
        for (Merchant m : merchants) {
            if (m.getLatitude() != null && m.getLongitude() != null) {
                Double distance = MapUtils.distance(m.getLongitude(), m.getLatitude(), longitude, latitude);
                map.put(m, distance);
            }
        }
        //排序
        List<Map.Entry<Merchant, Double>> list = new ArrayList<>(map.entrySet());
        list.sort(Comparator.comparing(Map.Entry<Merchant, Double>::getValue));
        List<Merchant> mers = new ArrayList<>();
        for (Map.Entry<Merchant, Double> m : list) {
            mers.add(m.getKey());
        }*/

        //距离排序
        //List<Merchant> mers = this.distanceSorting(merchants, longitude, latitude, null);

        Map<Merchant, Double> mers = this.distanceSorting(merchants, longitude, latitude, null);

/*        if (popularTag == null) {
            popularTag = pageQuery.getSearch();
        }*/

        //转DTO
        List<MerchantDTO> merchantDTOS = CollUtil.newArrayList();

        for (Map.Entry<Merchant, Double> m : mers.entrySet()) {

            //  }
            // for (Merchant m : mers) {
            MerchantSettings settings = merchantSettingsRepo.findByMerchantId(m.getKey().getId())
                    .orElseThrow(new BusinessException("商户不存在"));
            MerchantDTO merchantDTO = new MerchantDTO(m.getKey(), settings, m.getValue());
            if (StrUtil.isNotBlank(popularTag)) {
                merchantDTO.setFullReductions(fullReductionRepo.findAllByMerchantId(merchantDTO.getMid()));
                switch (popularTag) {
                    case "首单立减":
                        if ((settings.getFirstOrder() != null ? settings.getFirstOrder()
                                .compareTo(BigDecimal.ZERO) : 0) > 0) {
                            merchantDTOS.add(merchantDTO);
                        }
                        break;
                    case "满减优惠":
                        if (CollUtil.isNotEmpty(fullReductionRepo.findAllByMerchantId(m.getKey().getId()))) {
                            merchantDTOS.add(merchantDTO);
                        }
                        break;
                    case "折扣商家":
                        if (StrUtil.isNotEmpty(merchantClassificationRepo.findByMerchantIdAndType(m.getKey().getId(), 2)
                                .getGoodsIds())) {
                            merchantDTOS.add(merchantDTO);
                        }
                        break;
                    case "下单返红包":
                        if (CollUtil.isNotEmpty(couponRepo.findAllByMerchantIdAndEnabledTrue(m.getKey().getId()))) {
                            merchantDTOS.add(merchantDTO);
                        }
                        break;
                    case "可用红包":
                        List<UserCoupon> coupons = userCouponRepo.findAllByUserIdAndIsUsedFalse(userId);
                        List<UserCoupon> collect = coupons.stream().filter(c ->
                                !c.getCoupon().getEndDate().isBefore(LocalDate.now()) && (c.getCoupon()
                                        .getMerchantId() == null || c.getCoupon()
                                        .getMerchantId()
                                        .equals(m.getKey().getId()))
                        ).collect(Collectors.toList());
                        if (CollUtil.isNotEmpty(collect)) {
                            merchantDTOS.add(merchantDTO);
                        }
                        break;

                    default:
                        merchantDTOS.add(merchantDTO);
                        break;
                }
            } else {
                merchantDTO.setFullReductions(fullReductionRepo.findAllByMerchantId(merchantDTO.getMid()));
                merchantDTOS.add(merchantDTO);
            }
        }

//        new PageImpl<>(merchantDTOS, toPageRequest(pageQuery), merchantDTOS.size())
        return merchantDTOS;
    }


    /**
     * 显示个人的商户信息
     *
     * @param userId 用户的ID
     * @return 个人的商户信息
     */
    public MerchantDTO my(Long userId) {
        Merchant merchant = merchantRepo.findByUserId(userId).orElseThrow(new BusinessException("商户不存在"));
        MerchantSettings merchantSettings = merchantSettingsRepo.findByMerchantId(merchant.getId())
                .orElseThrow(new BusinessException("商户不存在"));
        return new MerchantDTO(merchant, merchantSettings);
    }

    /**
     * 用户查找商家
     *
     * @param userId 用户ID
     * @return 商家ID
     */
    public Long findMerchantId(Long userId) {
        Merchant merchant = merchantRepo.findByUserId(userId).orElseThrow(new BusinessException("商户不存在"));
        return merchant.getId();
    }

    /*
    开始/停止营业
     */
    public void closeMer(Long merchantId) {
        MerchantSettings merchant = merchantSettingsRepo.findByMerchantId(merchantId)
                .orElseThrow(new BusinessException("无商户"));
        if (merchant.getIsOpening()) {
            merchant.setIsOpening(false);
        } else {
            merchant.setIsOpening(true);
        }
        merchantSettingsRepo.save(merchant);
    }

    /**
     * 距离排序,如果有范围，范围内进行排序
     *
     * @param merchants 商家列表
     * @param longitude 经度
     * @param latitude  纬度
     * @param range     范围
     * @return 商家及距离
     */
    public Map<Merchant, Double> distanceSorting(Set<Merchant> merchants, Double longitude, Double latitude, Double range) {
        //所有商家要按距离排序规则
        Map<Merchant, Double> map = new HashMap<>();
        //算距离
        for (Merchant m : merchants) {
            if (ObjectUtil.isNotNull(m.getLatitude()) && ObjectUtil.isNotNull(m.getLongitude())) {
                Double distance = MapUtils.distance(m.getLongitude(), m.getLatitude(), longitude, latitude);
                if (range != null) {
                    if (distance < range) {
                        map.put(m, distance);
                    }
                } else {
                    map.put(m, distance);
                }
            }
        }

        //排序
        List<Map.Entry<Merchant, Double>> list = new ArrayList<>(map.entrySet());
        list.sort(Comparator.comparing(Map.Entry<Merchant, Double>::getValue));
        //List<Merchant> mers = new ArrayList<>();

        Map<Merchant, Double> merchantMap = new HashMap<>();
        for (Map.Entry<Merchant, Double> m : list) {
            //mers.add(m.getKey());
            merchantMap.put(m.getKey(), m.getValue());
        }

        return merchantMap;
    }

    /**
     * 带距离的商家
     *
     * @param merchants 商户列表
     * @param longitude 经度
     * @param latitude  纬度
     * @param range     范围
     * @return 商家列表
     */
    public List<MerchantDTO> withDistance(Set<Merchant> merchants, Double longitude, Double latitude, Double range) {
        Map<Merchant, Double> merchantMap = this.distanceSorting(merchants, longitude, latitude, range);
        List<MerchantDTO> dtos = new ArrayList<>();
        for (Map.Entry<Merchant, Double> m : merchantMap.entrySet()) {
            merchantSettingsRepo.findByMerchantId(m.getKey().getId())
                    .ifPresent(s -> dtos.add(new MerchantDTO(m.getKey(), s, m.getValue())));
        }
        return dtos;
    }
}
