package com.xxfc.platform.campsite.biz;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.util.RandomUtil;
import com.github.wxiaoqi.security.common.vo.GoodDataVO;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import com.xxfc.platform.campsite.dto.*;
import com.xxfc.platform.campsite.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import com.xxfc.platform.campsite.entity.CampsiteShop;
import com.xxfc.platform.campsite.mapper.CampsiteShopMapper;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 营地店铺表
 *
 * @author libin
 * @email 18178966185@163.com
 * @date 2019-06-17 10:28:48
 */
@Service
@Slf4j
public class CampsiteShopBiz extends BaseBiz<CampsiteShopMapper, CampsiteShop> {

    @Autowired
    private CampsiteShopTagBiz campsiteShopTagBiz;

    @Autowired
    private CampsiteShopCarouselBiz campsiteShopCarouselBiz;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Resource(name = "customRedisTemplate")
    HashOperations<String, String, String> campHashOperations;

    @Resource(name = "customRedisTemplate")
    ValueOperations<String, String> campsiteValueOperations;

    /**
     * 营地缓存前缀
     */
    private static final String CAMPSITE_LIST_CACHE_PREKEY = "campsite_cache:";
    private static final String CAMSITE_DETAIL_CACHE_PREKEY = "campsite:detail:cache:";
    private static final long CAMPSITE_CACHE_EXPIRE_TIME = 6000;

    /**
     * 地球半径,单位 km
     */
    private static final double EARTH_RADIUS = 6378.137;

    /**
     * 首页营地列表
     *
     * @param page
     * @param limit
     * @return
     */
    public List<GoodDataVO> getAllByHome(Integer page, Integer limit) {
        return mapper.findAllByHome((page - 1) * limit, limit);
    }


    public PageDataVO<CampsiteShopPageVo> findCampsiteShopPageByType(CampsiteShopFindDTO campsiteShopFindDTO) {
        String campsite_cache_key = String.format("%s%s%s%s%s", campsiteShopFindDTO.getType() == null ? "" : campsiteShopFindDTO.getType() + ":",
                campsiteShopFindDTO.getAddrProvince() == null ? "" : campsiteShopFindDTO.getAddrProvince() + ":",
                campsiteShopFindDTO.getAddrCity()==null?"":campsiteShopFindDTO.getAddrCity()+":",
                campsiteShopFindDTO.getPage()+":",
                campsiteShopFindDTO.getLimit());
        String  result = campHashOperations.get(CAMPSITE_LIST_CACHE_PREKEY,campsite_cache_key);
        if (result != null) {
            return JSONObject.parseObject(result, new TypeReference<PageDataVO<CampsiteShopPageVo>>() {
            });
        }
        PageDataVO<CampsiteShopPageVo> campsiteShopPageDataVO = new PageDataVO<>();

        PageDataVO<CampsiteShopPageDTO> pageDataVO = PageDataVO.pageInfo(campsiteShopFindDTO.getPage(), campsiteShopFindDTO.getLimit(), () -> mapper.findAllCampsiteShopsByTypeOrCode(campsiteShopFindDTO.getType(),campsiteShopFindDTO.getAddrProvince(),campsiteShopFindDTO.getAddrCity()));
        List<CampsiteShopPageDTO> campsiteShopPageDTOS = pageDataVO.getData();
        if (CollectionUtils.isEmpty(campsiteShopPageDTOS)) {
            campsiteShopPageDataVO.setPageNum(campsiteShopFindDTO.getPage());
            campsiteShopPageDataVO.setTotalCount(0L);
            campsiteShopPageDataVO.setTotalPage(0);
            campsiteShopPageDataVO.setPageSize(campsiteShopFindDTO.getLimit());
            campsiteShopPageDataVO.setData(Collections.EMPTY_LIST);
            return campsiteShopPageDataVO;
        }
        if (log.isDebugEnabled()) {
            log.debug("根据type=【{}】查询到的店铺数据：【{}】", campsiteShopFindDTO.getType(), campsiteShopPageDTOS);
        }

        List<CampsiteShopPageVo> campsiteShopPageVoList = new ArrayList<>();
        campsiteShopPageDTOS = campsiteShopPageDTOS.stream().distinct().collect(Collectors.toList());
        for (CampsiteShopPageDTO campsiteShopPageDTO : campsiteShopPageDTOS) {
            CampsiteShopPageVo campsiteShopPageVo = new CampsiteShopPageVo();
            BeanUtils.copyProperties(campsiteShopPageDTO, campsiteShopPageVo);
            campsiteShopPageVoList.add(campsiteShopPageVo);
        }
        campsiteShopPageVoList.sort(Comparator.comparing(CampsiteShopPageVo::getHot).reversed().thenComparing(CampsiteShopPageVo::getCrtTime).reversed());

        campsiteShopPageDataVO.setTotalPage(pageDataVO.getTotalPage());
        campsiteShopPageDataVO.setTotalCount(pageDataVO.getTotalCount());
        campsiteShopPageDataVO.setPageSize(pageDataVO.getPageSize());
        campsiteShopPageDataVO.setPageNum(pageDataVO.getPageNum());
        campsiteShopPageDataVO.setData(campsiteShopPageVoList);

        campHashOperations.put(CAMPSITE_LIST_CACHE_PREKEY,campsite_cache_key,JSONObject.toJSONString(campsiteShopPageDataVO));
        return campsiteShopPageDataVO;
    }

    /**
     * @param id
     * @param longitude 经度
     * @param latitude  纬度
     * @return
     */
    public CampsiteShopDetailVo findCampsiteShopDetailById(Integer id, Double longitude, Double latitude) {

        String result = campsiteValueOperations.get(String.format("%s%d", CAMSITE_DETAIL_CACHE_PREKEY, id));
        if (Objects.nonNull(result)) {
            return JSONObject.parseObject(result, new TypeReference<CampsiteShopDetailVo>() {
            });
        }

        CampsiteShopDetailVo campsiteShopDetailVo = new CampsiteShopDetailVo();
        CampsiteShopDetailDTO campsiteShopDetailDTO = mapper.findCampsiteShopDetailById(id);
        if (log.isDebugEnabled()) {
            log.debug("根据店铺id=【{}】查询出店铺信息【{}】", id, campsiteShopDetailDTO);
        }
        if (Objects.isNull(campsiteShopDetailDTO)) {
            return campsiteShopDetailVo;
        }
        BeanUtils.copyProperties(campsiteShopDetailDTO, campsiteShopDetailVo);
        List<CampsiteShopCarouselDetailDTO> campsiteShopCarouselDTOS = campsiteShopCarouselBiz.findByCampsiteShopId(id);
        if (log.isDebugEnabled()) {
            log.debug("根据店铺id=【{}】查询出店铺轮播图信息：【{}】", id, campsiteShopCarouselDTOS);
        }
        List<CampsiteShopCarouselDetailVo> campsiteShopCarouselDetailVos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(campsiteShopCarouselDTOS)) {
            campsiteShopCarouselDTOS.forEach(campsiteShopCarouselDetailDTO -> {
                CampsiteShopCarouselDetailVo carouselDetailVo = new CampsiteShopCarouselDetailVo();
                BeanUtils.copyProperties(campsiteShopCarouselDetailDTO, carouselDetailVo);
                campsiteShopCarouselDetailVos.add(carouselDetailVo);
            });
        }
        campsiteShopDetailVo.setCampsiteShopCarouselDetailVos(campsiteShopCarouselDetailVos);
        List<CampsiteShopTagDTO> shopTagDTOS = campsiteShopTagBiz.findByCampsiteShopId(id);
        if (log.isDebugEnabled()) {
            log.debug("根据店铺id=【{}】查询出店铺类型:【{}】", id, shopTagDTOS);
        }
        campsiteShopDetailVo.setTypeNames(shopTagDTOS == null ? Collections.EMPTY_LIST : shopTagDTOS.stream().map(CampsiteShopTagDTO::getName).collect(Collectors.toList()));
        //根据经纬度算距离
        if(Objects.nonNull(latitude) && Objects.nonNull(longitude)) {
            if (campsiteShopDetailDTO.getLongitude() != null && campsiteShopDetailDTO.getLatitude() != null) {
                double distance = getDistance(campsiteShopDetailDTO.getLongitude(), campsiteShopDetailDTO.getLatitude(), longitude, latitude);
                if (log.isDebugEnabled()) {
                    log.debug("根据店铺经度=【{}】,纬度=【{}】和自己所在位置的经度=【{}】,纬度=【{}】计算出的距离：【{}km】", campsiteShopDetailDTO.getLongitude(), campsiteShopDetailDTO.getLatitude(), longitude, latitude, distance);
                }
                campsiteShopDetailVo.setDistance(String.format("%.1f", distance));
            }
        }
        campsiteValueOperations.set(String.format("%s%d", CAMSITE_DETAIL_CACHE_PREKEY, id), JSONObject.toJSONString(campsiteShopDetailVo));
        return campsiteShopDetailVo;
    }


    public PageDataVO<CampsiteShopAdminPageVo> findCampsiteShopPage(CampsiteShopAdminFindDTO campsiteShopAdminFindDTO) {
        if (log.isDebugEnabled()) {
            log.debug("查询条件：【{}】", campsiteShopAdminFindDTO);
        }
        PageDataVO<CampsiteShopAdminPageVo> campsiteShopAdminPageDataVos = new PageDataVO<>();

        PageDataVO<CampsiteShopAdminPageDTO> campsiteShopAdminpageDTOPageDataVO = PageDataVO.pageInfo(campsiteShopAdminFindDTO.getPage(), campsiteShopAdminFindDTO.getLimit(), () -> mapper.findAllCampsiteShops(campsiteShopAdminFindDTO));
        List<CampsiteShopAdminPageDTO> campsiteShopAdminPageDTOS = campsiteShopAdminpageDTOPageDataVO.getData();
        if (log.isDebugEnabled()) {
            log.debug("查询结果：【{}】", campsiteShopAdminPageDTOS);
        }
        if (CollectionUtils.isEmpty(campsiteShopAdminPageDTOS)) {
            return campsiteShopAdminPageDataVos;
        }

        List<Integer> campsiteShopIds = campsiteShopAdminPageDTOS.stream().map(CampsiteShopAdminPageDTO::getId).collect(Collectors.toList());
        //根据营地ids查询营地对应的标签  键营地id  值对应的标签列表
        Map<Integer, List<String>> shopIdOfTagsMap = campsiteShopTagBiz.findByCampsiteShopIds(campsiteShopIds);
        if (log.isDebugEnabled()) {
            log.debug("营地id为:【{}】的类型【{}】", campsiteShopIds, shopIdOfTagsMap);
        }
        campsiteShopAdminPageDTOS = campsiteShopAdminPageDTOS.stream().peek(campsiteShopAdminPageDTO -> {
            if (Objects.nonNull(shopIdOfTagsMap)) {
                List<String> tagNames = shopIdOfTagsMap.get(campsiteShopAdminPageDTO.getId());
                campsiteShopAdminPageDTO.setStoreTypeName(tagNames == null ? new ArrayList<String>() : tagNames);
            }
        }).sorted(Comparator.comparing(CampsiteShopAdminPageDTO::getHot).reversed().thenComparing(CampsiteShopAdminPageDTO::getCrtTime).reversed()).collect(Collectors.toList());

        //组装数据
        campsiteShopAdminPageDataVos.setTotalPage(campsiteShopAdminpageDTOPageDataVO.getTotalPage());
        campsiteShopAdminPageDataVos.setTotalCount(campsiteShopAdminpageDTOPageDataVO.getTotalCount());
        campsiteShopAdminPageDataVos.setPageSize(campsiteShopAdminpageDTOPageDataVO.getPageSize());
        campsiteShopAdminPageDataVos.setPageNum(campsiteShopAdminpageDTOPageDataVO.getPageNum());
        campsiteShopAdminPageDataVos.setData(JSONObject.parseObject(JSONObject.toJSONString(campsiteShopAdminPageDTOS), new TypeReference<List<CampsiteShopAdminPageVo>>() {
        }));
        return campsiteShopAdminPageDataVos;
    }


    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int saveCampsiteShop(CampsiteShopAdminDTO campsiteShopAdminDTO) {
        CampsiteShop campsiteShop = new CampsiteShop();
        campsiteShopAdminDTO.setName(campsiteShopAdminDTO.getName() == null ? null : campsiteShopAdminDTO.getName().trim());
        BeanUtils.copyProperties(campsiteShopAdminDTO, campsiteShop);
        int effectRows = 0;
        if (Objects.nonNull(campsiteShopAdminDTO.getId())) {
            if (log.isDebugEnabled()) {
                log.debug("更新营地的信息：【{}】", campsiteShop);
            }
            //更新操作
            campsiteShop.setUpdTime(Instant.now().toEpochMilli());
            effectRows = mapper.updateByPrimaryKeySelective(campsiteShop);
        } else {
            //保存操作
            //保存营地信息
            if (log.isDebugEnabled()) {
                log.debug("保存营地的信息：【{}】", campsiteShop);
            }
            campsiteShop.setCrtTime(Instant.now().toEpochMilli());
            effectRows = mapper.insertSelective(campsiteShop);
        }
        //保存或更新 * 营地与 轮播图信息
        List<CampsiteShopCarouselDTO> carouselDTOS = campsiteShopAdminDTO.getCarouselDTOS();
        if (CollectionUtils.isNotEmpty(carouselDTOS)) {
            carouselDTOS.stream().peek(campsiteShopCarouselDTO -> campsiteShopCarouselDTO.setShopId(campsiteShop.getId())).count();
        }
        campsiteShopCarouselBiz.saveBatch(carouselDTOS, campsiteShop.getId());

        //保存或更新 * 营地与营地类型信息
        List<Integer> campsiteTagDTOS = campsiteShopAdminDTO.getCampsiteTagDTOS();
        campsiteShopTagBiz.saveBatch(campsiteTagDTOS, campsiteShop.getId());
        redisTemplate.delete(CAMPSITE_LIST_CACHE_PREKEY);
        return effectRows;
    }


    public CampsiteShopAdminVO findCampsiteById(Integer id) {
        CampsiteShopAdminVO campsiteShopAdminVO = new CampsiteShopAdminVO();
        CampsiteShop campsiteShop = new CampsiteShop();
        campsiteShop.setId(id);
        CampsiteShop shop = mapper.selectOne(campsiteShop);
        BeanUtils.copyProperties(shop, campsiteShopAdminVO);

        //根据营地id查询轮播
        List<CampsiteShopCarouselDetailDTO> shopCarouselDetailDTOS = campsiteShopCarouselBiz.findByCampsiteShopId(id);
        List<CampsiteShopCarouselVo> campsiteShopCarouselVos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(shopCarouselDetailDTOS)) {
            shopCarouselDetailDTOS.forEach(campsiteShopCarouselDetailDTO -> {
                CampsiteShopCarouselVo campsiteShopCarouselVo = new CampsiteShopCarouselVo();
                BeanUtils.copyProperties(campsiteShopCarouselDetailDTO, campsiteShopCarouselVo);
                campsiteShopCarouselVos.add(campsiteShopCarouselVo);
            });
        }
        campsiteShopAdminVO.setCarouse(campsiteShopCarouselVos);
        //根据营地id查询营地类型
        List<CampsiteShopTagDTO> shopTagDTOS = campsiteShopTagBiz.findByCampsiteShopId(id);
        List<CampsiteTagListVo> shopTagVos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(shopTagDTOS)) {
            shopTagDTOS.forEach(campsiteShopTagDTO -> {
                CampsiteTagListVo campsiteTagListVo = new CampsiteTagListVo();
                BeanUtils.copyProperties(campsiteShopTagDTO, campsiteTagListVo);
                shopTagVos.add(campsiteTagListVo);
            });
            campsiteShopAdminVO.setCampsiteTagListVos(shopTagVos);
        }
        campsiteShopAdminVO.setCarouse(campsiteShopAdminVO.getCarouse() == null ? new ArrayList<CampsiteShopCarouselVo>() : campsiteShopAdminVO.getCarouse());
        campsiteShopAdminVO.setCampsiteTagListVos(campsiteShopAdminVO.getCampsiteTagListVos() == null ? new ArrayList<CampsiteTagListVo>() : campsiteShopAdminVO.getCampsiteTagListVos());
        redisTemplate.delete(String.format("%s%d", CAMSITE_DETAIL_CACHE_PREKEY, id));
        redisTemplate.delete(CAMPSITE_LIST_CACHE_PREKEY);
        return campsiteShopAdminVO;
    }

    /**
     * 逻辑删除
     *
     * @param id
     * @return
     */
    public int updateCampsiteSatus(int id) {
        int effectRows = mapper.updateCampsiteStatusById(id, 1);
        if (effectRows > 0) {
            redisTemplate.delete(String.format("%s%d", CAMSITE_DETAIL_CACHE_PREKEY, id));
            redisTemplate.delete(CAMPSITE_LIST_CACHE_PREKEY);
        }
        return effectRows;
    }


    public int upperOrLowerShelves(Integer id, Integer status) {
        int effectRows = mapper.updateCampsiteSaleStatusById(id, status);
        if (effectRows > 0) {
            redisTemplate.delete(String.format("%s%d", CAMSITE_DETAIL_CACHE_PREKEY, id));
            redisTemplate.delete(CAMPSITE_LIST_CACHE_PREKEY);
        }
        return effectRows;
    }


    public Boolean checkCampsiteNameExist(Integer id, String name) {
        int count = mapper.checkNameExist(id, name);
        if (count == 0) {
            return false;
        }
        return true;
    }

    private long transformStartTime(Long startTime) {
        return LocalDateTime.ofInstant(new Date(startTime).toInstant(), ZoneOffset.ofHours(+8))
                .withHour(0)
                .withMinute(0)
                .withSecond(0)
                .withNano(0)
                .toInstant(ZoneOffset.ofHours(+8))
                .toEpochMilli();
    }

    /**
     * 获取指定数量的随机旅游路线
     * @return
     */
    public ObjectRestResponse findRandomVehicle(Integer number) {
        number = number == null ? 2 : number;
        List<GoodDataVO> list = mapper.findAll();
        Set<GoodDataVO> resultList = new HashSet<>();
        if(CollectionUtils.isNotEmpty(list)) {
            if(number >= list.size()) {
                return ObjectRestResponse.succ(list);
            }
            Set<Integer> set = new HashSet<>();
            RandomUtil.randomSet(list.size(), number, set, number);
            for(Integer i : set) {
                resultList.add(list.get(i));
            }
        }
        return ObjectRestResponse.succ(resultList);
    }



    private long transformEndTime(Long endTime) {
        return LocalDateTime.ofInstant(new Date(endTime).toInstant(), ZoneOffset.ofHours(+8))
                .withHour(23)
                .withMinute(59)
                .withSecond(59)
                .withNano(999)
                .toInstant(ZoneOffset.ofHours(+8))
                .toEpochMilli();
    }

    /**
     * 根据经纬度，计算两点间的距离
     *
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @return 返回距离 单位千米
     */
    private static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        // 纬度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 经度
        double lng1 = Math.toRadians(longitude1);
        double lng2 = Math.toRadians(longitude2);
        // 纬度之差
        double a = lat1 - lat2;
        // 经度之差
        double b = lng1 - lng2;
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘地球半径, 返回单位: 千米
        s = s * EARTH_RADIUS;
        return s;
    }
}