package com.sv.service.api;

import com.enums.EnterVenuePayTypeEnum;
import com.enums.MoneyLogEnum;
import com.enums.PayTypeEnum;
import com.enums.VipTypeEnum;
import com.github.pagehelper.PageHelper;
import com.sv.dto.EnterResult;
import com.sv.dto.api.MemberCardDTO;
import com.sv.dto.api.MemberCardVenuesDTO;
import com.sv.dto.api.VenueDTO;
import com.sv.entity.*;
import com.sv.exception.api.ExceptionCodeTemplate;
import com.sv.mapper.VenueMapper;
import com.sv.service.api.util.DateUtilCard;
import com.sv.service.api.util.GeoHashUtils;
import com.sv.service.common.FaceService;
import com.sv.service.common.OSSClientUtil;
import com.sv.service.common.PlatformService;
import com.sv.service.oms.DeviceService;
import com.ydd.framework.core.common.Pagination;
import com.ydd.framework.core.common.utils.ValidationUtils;
import com.ydd.framework.core.exception.ServiceException;
import com.ydd.framework.core.service.CacheService;
import com.ydd.framework.core.service.impl.BaseServiceImpl;
import com.ydd.framework.core.service.impl.WxXCXService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

/**
 * Service - 场馆
 *
 * @author zuojie.Liang
 * @since 2018-08-02
 */
@Service("VenueServiceApi")
@Transactional(readOnly = true)
public class VenueService extends BaseServiceImpl {

    private final Logger logger = LoggerFactory.getLogger(VenueService.class);

    @Resource
    private VenueMapper venueMapper;

    @Resource
    private MemberFaceInfoService memberFaceInfoService;

    @Resource
    private MemberCardService memberCardService;

    @Resource
    private MemberService memberService;

    @Resource
    private MemberEnterVenueLogService memberEnterVenueLogService;

    @Resource(name = "OrderServiceApi")
    private OrderService orderService;

    @Resource
    private DeviceService deviceService;
    @Resource
    private MemberMoneyLogService memberMoneyLogService;
    @Resource
    private VenuePriceService venuePriceService;

    @Resource
    private FaceService faceService;


    /**
     * 更新场馆
     *
     * @param venue 场馆
     */
    @Transactional
    public void update(Venue venue) {
        venueMapper.update(venue);
    }

    /**
     * 删除场馆
     *
     * @param id 编号
     * @return 删除数量
     */
    @Transactional
    public Integer delete(Integer id) {
        if (id == null || id <= 0) {
            return 0;
        }
        return venueMapper.delete(id);
    }

    /**
     * 删除场馆
     *
     * @param ids 编号数组
     * @return 删除数量
     */
    @Transactional
    public Integer deleteByIds(Integer[] ids) {
        if (ids == null || ids.length == 0) {
            return 0;
        }
        return venueMapper.deleteByIds(ids);
    }

    /**
     * 查询场馆
     *
     * @param id 编号
     * @return 场馆
     */
    public Venue findById(Integer id) {
        return venueMapper.findById(id);
    }

    /**
     * api 查询场馆
     *
     * @param id 编号
     * @return 场馆
     */
    public VenueDTO findByVenueId(Integer id) {
        return venueMapper.findByVenueId(id);
    }

    /**
     * 查询场馆
     *
     * @param pagination 分页信息
     * @return 分页结果
     */
    public Pagination findPage(Pagination pagination) {
        PageHelper.startPage(pagination.getPage(), pagination.getPageSize());
        PageHelper.orderBy("id desc");
        pagination.setQueryResult(venueMapper.findAll());
        return pagination;
    }

    /**
     * api查询场馆
     *
     * @param pagination 分页信息
     * @param longitude
     * @param latitude   @return 分页结果
     */
    public Pagination findPages(Pagination pagination, Integer type, String longitude, String latitude) {
        PageHelper.startPage(pagination.getPage(), pagination.getPageSize());
        List<VenueDTO> venueDTOS = new ArrayList<>();
        venueDTOS = venueMapper.findAllVenues(type, null);
        for (VenueDTO venue : venueDTOS) {
            calculateDistance(venue, longitude, latitude);//计算距离
        }
        // 排序
        Collections.sort(venueDTOS, new Comparator<VenueDTO>() {
            public int compare(VenueDTO o1, VenueDTO o2) {
                if (o1.getDistance().compareTo(o2.getDistance()) == 1) {
                    return 1;
                }
                if (o1.getDistance() == o2.getDistance()) {
                    return 0;
                }
                return -1;
            }
        });
        pagination.setData(venueDTOS);
        return pagination;
    }

    /**
     * 通过地图查询场馆
     *
     * @param zoom 地图缩放等级
     */
    public List<VenueDTO> findVenuesForMap(Integer type, String longitude, String latitude, Integer zoom) {
        List<String> geoHashList = new ArrayList<>();
        if (isCoordinateEffective(longitude, latitude)) {
            // 编码长度4位的经度为20km
            // 编码长度5位的经度为2.4km
            geoHashList.add(GeoHashUtils.encodeHash(longitude, latitude, zoom));
            geoHashList.addAll(GeoHashUtils.neighbours(geoHashList.get(0)));
            for (String neighbours : geoHashList) {
                System.out.println(neighbours);
            }
        }
        List<VenueDTO> venueDTOS = new ArrayList<>();
        if (geoHashList.size() == 0) {
            // 若经纬度不存在，则只查询一次
            venueDTOS = venueMapper.findAllVenues(type, null);
        } else {
            // 通过geohash查询
            for (String geoHash : geoHashList) {
                venueDTOS.addAll(venueMapper.findAllVenues(type, geoHash));
            }
        }
        for (VenueDTO venue : venueDTOS) {
            calculateDistance(venue, longitude, latitude);//计算距离，内存排序，subList
        }
        return venueDTOS;
    }

    /**
     * api 接口
     * 我要进场
     */
    public Integer join(Integer memberId, Integer venueId) {

        Integer flag = 0;
        // 判断用户是否登录
        if (memberId == null) {
            throw new ServiceException(com.ydd.framework.core.exception.ExceptionCodeTemplate.NEED_LOGIN);
        }
        //参数校验
        ValidationUtils.assertNotNull(venueId);

        /*// 1.判断用户有没有人脸识别的图像,根据用户ID查找，一个用户只能有一条
        MemberFaceInfo memberFaceInfo = memberFaceInfoService.findByMemberId(memberId);
        if (memberFaceInfo == null) {
            flag = 1;
            return flag;
        }*/

        // 1.判断用户有没有该馆的会员卡(有效期内，如果有免费卡和别的卡，优先使用别的卡)，有不判断余额
        List<MemberCard> memberCards = memberCardService.findByMemberId(venueId, memberId);

        // 2. 查询该场馆需要的费用,并比较用户余额是否足够
        // 查询当前时间内，场馆对应的价格
        String time = DateUtilCard.nowTime().toString();
        VenuePrice venuePrice = venuePriceService.findPrice(venueId, time);
        if (venuePrice == null) {
            throw new ServiceException(ExceptionCodeTemplate.VENUE_ERROR);
        }
        Venue venue = venueMapper.findById(venueId);
        if (memberCards.size() == 0) {
            Member member = memberService.findById(memberId);
            if (member.getMoney().compareTo(venuePrice.getPrice()) == -1) {
                flag = 2;
                return flag;
            }
        }

        // 3.判断用户有没有人脸识别的图像,根据用户ID查找，一个用户只能有一条
        MemberFaceInfo memberFaceInfo = memberFaceInfoService.findByMemberId(memberId);
        if (memberFaceInfo == null) {
            flag = 1;
            return flag;
        }

        return flag;
    }

    /**
     * api
     * 查询我的会员卡
     */
    List<MemberCardVenuesDTO> findAVenues(Member member) {
        List<MemberCardVenuesDTO> venuesDTOS = venueMapper.findByMemberCard(member.getPlatformId(), member.getId());
        for (MemberCardVenuesDTO venuesDTO : venuesDTOS) {
            List<MemberCardDTO> cardDTOS = venuesDTO.getMemberCards();
            for (MemberCardDTO cardDTO : cardDTOS) {
                if (cardDTO.getEndTime().getTime() < System.currentTimeMillis()) {
                    cardDTO.setCardFlg(2);
                }
                if (cardDTO.getStartTime().getTime() > System.currentTimeMillis()) {
                    cardDTO.setCardFlg(1);
                }
            }
        }

        return venuesDTOS;
    }


    private void calculateDistance(VenueDTO venue, String longitude, String latitude) {
        // 计算距离
        if (isCoordinateEffective(longitude, latitude)) {
            venue.setDistance(GeoHashUtils.distance(
                    Double.valueOf(latitude),
                    Double.valueOf(longitude),
                    Double.parseDouble(venue.getLatitude()),
                    Double.parseDouble(venue.getLongitude())));
        } else {
            venue.setDistance(BigDecimal.ZERO);
        }
    }


    /**
     * 判断坐标点是否有效
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return 有效返回 {@code true}，反之 {@code false}
     */
    private boolean isCoordinateEffective(String longitude, String latitude) {
        return StringUtils.isNotBlank(longitude) && StringUtils.isNotBlank(latitude);
    }

    /**
     * 用户入场
     *
     * @param faceId
     * @param deviceId
     * @return 0成功 -1失败
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized EnterResult enterVenue(Integer faceId, Integer deviceId) {
        EnterResult result = new EnterResult();
        synchronized (("enter" + faceId.toString()).intern()) {
            Date now = new Date();
            Device device = deviceService.findById(deviceId);
            Venue venue = venueMapper.findById(device.getVenueId());
            // 查询当前时间内，场馆对应的价格
            String time = DateUtilCard.nowTime().toString();
            VenuePrice venuePrice = venuePriceService.findPrice(device.getVenueId(), time);
            if (venuePrice == null) {
                logger.info("场馆暂未开放");
                result.setStatus(-2);
                return result;
            }
            if (venue.getStatus().intValue() == 1) {
                logger.info(venue.getName() + "被禁用，入场失败");
                result.setStatus(-2);
                return result;
            }
            Member member = memberService.findByFaceId(faceId);
            memberService.verify(member);
            if (venue == null || member == null) {
                result.setStatus(-1);
                return result;
            }
            //判断8小时之内 是否连续进场
            MemberEnterVenueLog enterVenueLog = memberEnterVenueLogService.findMemberLastLog(member.getId(), device.getVenueId());
            MemberEnterVenueLog log = new MemberEnterVenueLog();
            log.setMemberId(member.getId());
            log.setVeneuType(device.getVenueType());
            log.setVenueId(device.getVenueId());
            log.setPlatformId(member.getPlatformId());
            if (enterVenueLog != null) {
                if ((now.getTime() - enterVenueLog.getCreatedTime().getTime()) <= (8 * 60 * 60 * 1000)) {
                    //小于8小时直接进场
                    log.setOrderSn("");
                    logger.info("用户：" + member.getNickname() + "小于8小时直接进场");
                    memberEnterVenueLogService.save(log);
                    result.setStatus(0);
                    return result;
                }
            }
            //进场之前 查看是否有会员卡
            List<MemberCard> cards = memberCardService.findByMemberId(device.getVenueId(), member.getId());
            if (cards.size() > 0) {
                //有会员卡 查看是否是免费卡
                MemberCard useCard = null;
                for (MemberCard card : cards) {
                    if (!VipTypeEnum.FREECARD.value.equals(card.getCardType())) {
                        useCard = card;
                        break;
                    } else {
                        //查看该免费卡是否可用 (一天用一次)
                        if (memberEnterVenueLogService.isFreeCardUseful(card.getId())) {
                            useCard = card;
                            break;
                        }
                    }
                }
                if (useCard != null) {
                    //使用会员卡入场
                    logger.info("用户" + member.getNickname() + "使用" + useCard.getCardType() + "入场");
                    log.setMemberCardId(useCard.getId());
                    log.setPayType(EnterVenuePayTypeEnum.MEMBER_CARD.value);
                    String orderSn = orderService.createEnterVenueOrder(venue, member.getId(), PayTypeEnum.MEMBER_CARD, venuePrice.getPrice());
                    // 会员卡入场，增加记录
                    createMemberMoneyLog(MoneyLogEnum.JOIN.value, venue.getPrice(), member.getId(), member.getPlatformId(), PayTypeEnum.MEMBER_CARD.value, useCard.getCardType(),
                            venue.getId(), venue.getType());
                    log.setOrderSn(orderSn);
                    memberEnterVenueLogService.save(log);
                    result.setStatus(1);
                    return result;
                }
            }
            //没有会员卡 扣余额
            //判断余额是否够
            if (memberService.isMoneyEnough(member.getId(), venuePrice.getPrice())) {
                logger.info("用户" + member.getNickname() + "使用余额进场");
                result.setMoney(venuePrice.getPrice());
                String orderSn = orderService.createEnterVenueOrder(venue, member.getId(), PayTypeEnum.BALANCE, venuePrice.getPrice());
                log.setOrderSn(orderSn);
                log.setPayType(EnterVenuePayTypeEnum.WEI_XIN.value);
                memberEnterVenueLogService.save(log);
                result.setStatus(2);
                return result;
            } else {
                //余额不足
                logger.info("用户" + member.getNickname() + "余额不足进场失败");
                result.setStatus(-2);
                return result;
            }
        }
    }


    // 记录使用会员卡入场记录
    @Transactional
    public void createMemberMoneyLog(Integer type, BigDecimal price, Integer memberId, Integer platformId, Integer payType, String cardType,
                                     Integer venueId, Integer venueType) {
        MemberMoneyLog memberMoneyLog = new MemberMoneyLog();
        memberMoneyLog.setType(type);
        memberMoneyLog.setMoney(price.negate());
        memberMoneyLog.setMemberId(memberId);
        memberMoneyLog.setPlatformId(platformId);
        memberMoneyLog.setPayType(payType);
        memberMoneyLog.setCardType(cardType);
        memberMoneyLog.setVenueId(venueId);
        memberMoneyLog.setVenueType(venueType);
//        memberMoneyLog.setLessonId(lessonId);
        memberMoneyLogService.insert(memberMoneyLog);
    }

    @Transactional(rollbackFor = Exception.class)
    public void addNumber(Integer venueId, Integer number, Integer memberId) {
        if (number > 0 && venueMapper.countByMember(memberId, venueId) <= 0) {
            venueMapper.addNumber(number, venueId);
            venueMapper.addMember(memberId, venueId);
        } else if (number < 0 && venueMapper.countByMember(memberId, venueId) > 0) {
            venueMapper.addNumber(number, venueId);
            venueMapper.deleteMember(memberId, venueId);
        }
    }
}

