package com.xxfc.platform.user.behavior.biz;

import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.xxfc.platform.activity.dto.ActivityListDTO;
import com.xxfc.platform.activity.dto.ActivityPopularizeRelationDTO;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.user.behavior.common.BehaviorEnum;
import com.xxfc.platform.user.behavior.dto.ActivityBehaviorDTO;
import com.xxfc.platform.user.behavior.dto.ActivityBehaviorRelationDTO;
import com.xxfc.platform.user.behavior.dto.BehaviorTypeDTO;
import com.xxfc.platform.user.behavior.dto.CustomerBehaviorNoteDTO;
import com.xxfc.platform.user.behavior.entity.CustomerBehaviorNotes;
import com.xxfc.platform.user.behavior.mapper.CustomerBehaviorNotesMapper;
import com.xxfc.platform.user.behavior.vo.BehaviorNoteCollectVo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * 用户行为记录表
 *
 * @author libin
 * @email 18178966185@163.com
 * @date 2019-08-12 14:03:55
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor(onConstructor = @__({@Autowired}))
public class CustomerBehaviorNotesBiz extends BaseBiz<CustomerBehaviorNotesMapper, CustomerBehaviorNotes> {

    private final ActivityFeign activityFeign;

    private final BehaviorTypeBiz behaviorTypeBiz;

    private final ActivityBehaviorBiz activityBehaviorBiz;

    private final ActivityBehaviorRelationBiz activityBehaviorRelationBiz;

    @Value("${activity.register.id}")
    private Integer activityId;

    public void saveCustomerBehavior(CustomerBehaviorNoteDTO customerBehaviorNoteDTO) {
        CustomerBehaviorNotes customerBehaviorNotes = new CustomerBehaviorNotes();
        BeanUtils.copyProperties(customerBehaviorNoteDTO, customerBehaviorNotes);
        customerBehaviorNotes.setCrtTime(Instant.now().toEpochMilli());
        if (customerBehaviorNoteDTO.getType()==BehaviorEnum.REGISTRY.getCode()){
            customerBehaviorNotes.setTypeId(activityId);
        }
        mapper.insertSelective(customerBehaviorNotes);
    }

    public List<BehaviorNoteCollectVo> findBehaviorCollectByActivityId(Integer activityId, Long startTime, Long endTime) {

        List<BehaviorNoteCollectVo> behaviorNoteCollectVos = new ArrayList<>();

        //1.根据活动id查询活动行为
        List<ActivityBehaviorDTO> activityBehaviorDTOS = activityBehaviorBiz.findActivityBehaviorsByActivityId(activityId);
        if (CollectionUtils.isEmpty(activityBehaviorDTOS)) {
            return Collections.EMPTY_LIST;
        }
        List<Integer> behaviorTypeIds = activityBehaviorDTOS.stream().map(ActivityBehaviorDTO::getBehaviorTypeId).collect(Collectors.toList());
        //根据活动行为ids查询行为
        List<BehaviorTypeDTO> behaviorTypeDTOS = behaviorTypeBiz.findBehaviorTypesByIds(behaviorTypeIds);
        List<Integer> behaviorCodes = behaviorTypeDTOS.stream().map(BehaviorTypeDTO::getCode).collect(Collectors.toList());
        //过滤出活动行为的枚举类
        Set<BehaviorEnum> behaviorEnums = EnumSet.allOf(BehaviorEnum.class).stream().filter(behaviorEnum -> behaviorCodes.contains(behaviorEnum.getCode())).collect(Collectors.toSet());

        //2.获取时间间隔和开始时间与结束时间转换
        AtomicLong startAtomic = new AtomicLong(startTime == null ? 0 : startTime);
        AtomicLong endAtomic = new AtomicLong(endTime == null ? 0 : endTime);
        long between_day = getBetweenDayAndprocessStartTimeAndEndTime(activityId, startAtomic, endAtomic);

        //3.根据活动id 、开始时间、结束时间查询统计数据
        List<CustomerBehaviorNotes> customerBehaviorNotes = mapper.selectByActivityIdAndTime(activityId, startAtomic.get(), endAtomic.get());
        Map<Integer, List<CustomerBehaviorNotes>> behaviorAndDataMap = new HashMap<>(20);
        if (CollectionUtils.isNotEmpty(customerBehaviorNotes)) {
            behaviorAndDataMap = customerBehaviorNotes.stream().collect(Collectors.groupingBy(CustomerBehaviorNotes::getType, Collectors.toList()));
        }

        //4.邀请成功记录
        List<ActivityPopularizeRelationDTO> popularizeRelations = activityFeign.findActivityPopularizeRelationByActivityIdAndTime(activityId, startAtomic.get(), endAtomic.get());
        //5.查询banner点击 弹窗曝光，立即前往，精选活动数据
        List<ActivityBehaviorRelationDTO> activityBehaviorRelations = activityBehaviorRelationBiz.findActivityBehaviorRelationsByActivityId(activityId);
        Map<Integer, List<CustomerBehaviorNotes>> behaviorTypeAndDataOfBizMap = new HashMap<>(20);
        if (CollectionUtils.isNotEmpty(activityBehaviorRelations)) {
            List<Integer> bizIds = activityBehaviorRelations.stream().map(ActivityBehaviorRelationDTO::getBizId).collect(Collectors.toList());
            List<CustomerBehaviorNotes> customerBehaviorNotesbiz = mapper.selectAllByTypeIdsAndTime(bizIds, startAtomic.get(), endAtomic.get());
            //业务数据转换map
            if (CollectionUtils.isNotEmpty(customerBehaviorNotesbiz)) {
                behaviorTypeAndDataOfBizMap = customerBehaviorNotesbiz.stream().collect(Collectors.groupingBy(CustomerBehaviorNotes::getType, Collectors.toList()));
            }
        }

        //6.数据统计处理
        BehaviorNoteCollectVo behaviorNoteCollectVo;
        for (BehaviorEnum behaviorEnum : behaviorEnums) {
            behaviorNoteCollectVo = new BehaviorNoteCollectVo();
            behaviorNoteCollectVo.setBehavior(behaviorEnum.getName());
            long default_p_total, default_p_avg;
            ActionAcount actionAcount;
            switch (behaviorEnum) {
                //成功邀请人数统计
                case SUCCESS_INVIT:
                    actionAcount = new ActionAcount();
                    default_p_total = popularizeRelations.size();
                    default_p_avg = (default_p_total / between_day);
                    actionAcount.setDefault_p_total(default_p_total);
                    actionAcount.setDefault_p_avg(default_p_avg);
                    actionAcount.setDefault_u_total(default_p_total);
                    actionAcount.setDefault_u_avg(default_p_avg);
                    break;
                //成功邀请人数大于10的统计
                case SUCCESS_MORE_10_INVIT:
                    actionAcount = new ActionAcount();
                    default_p_total = popularizeRelations.stream().collect(Collectors.groupingBy(ActivityPopularizeRelationDTO::getMajorUserId, Collectors.counting())).values().stream().filter(x -> x >= 10).count();
                    default_p_avg = default_p_total / between_day;
                    actionAcount.setDefault_p_total(default_p_total);
                    actionAcount.setDefault_p_avg(default_p_avg);
                    actionAcount.setDefault_u_total(default_p_total);
                    actionAcount.setDefault_u_avg(default_p_avg);
                    break;
                //App访问量统计
                case APP_VISIT_COUNT:
                    actionAcount = new ActionAcount();
                    default_p_total = mapper.selectAppVvisitsByTypeAndTime(BehaviorEnum.APP_VISIT_COUNT.getCode(),startAtomic.get(),endAtomic.get());
                    default_p_avg = default_p_total / between_day;
                    actionAcount.setDefault_p_total(default_p_total);
                    actionAcount.setDefault_p_avg(default_p_avg);
                    actionAcount.setDefault_u_total(default_p_total);
                    actionAcount.setDefault_u_avg(default_p_avg);
                    break;
                //banner点击 精选活动 立即前往 弹窗曝光 数据统计
                case BANNER_CLICK:
                case WONDERFUL_ACTIVITY_CLICK:
                case DIALOG_WINDOW_TO:
                case DIALOG_WINDOW:
                    actionAcount = new ActionAcount(between_day, behaviorTypeAndDataOfBizMap).invoke(behaviorEnum);
                    break;
                //其它类型的数据统计
                default:
                    actionAcount = new ActionAcount(between_day, behaviorAndDataMap).invoke(behaviorEnum);
                    break;
            }
            behaviorNoteCollectVo.setP_count(actionAcount.getDefault_p_total());
            behaviorNoteCollectVo.setP_avg_count(actionAcount.getDefault_p_avg());
            behaviorNoteCollectVo.setU_count(actionAcount.getDefault_u_total());
            behaviorNoteCollectVo.setU_avg_count(actionAcount.getDefault_u_avg());
            behaviorNoteCollectVos.add(behaviorNoteCollectVo);
        }
        behaviorNoteCollectVos.sort(Comparator.comparing(BehaviorNoteCollectVo::getBehavior));
        return behaviorNoteCollectVos;
    }

    /**
     * 获取时间间隔 和 开始与结束时间的处理
     *
     * @param activityId 活动id
     * @param startTime  筛选的开始时间
     * @param endTime    筛选的结束时间
     * @return
     */
    private long getBetweenDayAndprocessStartTimeAndEndTime(Integer activityId, AtomicLong startTime, AtomicLong endTime) {
        if (startTime.get() == 0 || endTime.get() == 0) {
            ActivityListDTO activityListDTO = activityFeign.findActivityStartTimeAndEndTimeById(activityId);
            Instant now = Instant.now();
            if (startTime.get() != 0 && endTime.get() == 0) {
                endTime.set(now.toEpochMilli());
            }
            if (endTime.get() != 0 && startTime.get() == 0) {
                startTime.set(activityListDTO.getActivity_startTime());
            }
            if (startTime.get() == 0 && endTime.get() == 0) {
                startTime.set(activityListDTO.getActivity_startTime());
                endTime.set(now.toEpochMilli());
            }
        }
        long between_day = ChronoUnit.DAYS.between(Instant.ofEpochMilli(startTime.get()), Instant.ofEpochMilli(endTime.get()));
        return Math.abs(between_day) == 0 ? 1 : Math.abs(between_day);
    }

    private class ActionAcount {
        private long between_day;
        private Map<Integer, List<CustomerBehaviorNotes>> behaviorMap;
        private long default_p_total;
        private long default_p_avg;
        private long default_u_total;
        private long default_u_avg;

        public ActionAcount(long between_day, Map<Integer, List<CustomerBehaviorNotes>> behaviorMap) {
            this.between_day = between_day;
            this.behaviorMap = behaviorMap;
        }

        public ActionAcount() {
        }

        public ActionAcount invoke(BehaviorEnum behaviorEnum) {
            List<CustomerBehaviorNotes> behaviorTypeData = behaviorMap.get(behaviorEnum.getCode());
            boolean isEmpty = CollectionUtils.isEmpty(behaviorTypeData);
            default_p_total = isEmpty ? 0 : behaviorTypeData.size();
            default_p_avg = default_p_total / between_day;

            Set<CustomerBehaviorNotes> behaviorTypeDataSet = new HashSet<>(isEmpty ? Collections.EMPTY_SET : behaviorTypeData);
            default_u_total = behaviorTypeDataSet.size();
            default_u_avg = default_u_total / between_day;
            return this;
        }

        public long getDefault_p_total() {
            return default_p_total;
        }

        public long getDefault_p_avg() {
            return default_p_avg;
        }

        public long getDefault_u_total() {
            return default_u_total;
        }

        public long getDefault_u_avg() {
            return default_u_avg;
        }

        public void setBetween_day(long between_day) {
            this.between_day = between_day;
        }

        public void setBehaviorMap(Map<Integer, List<CustomerBehaviorNotes>> behaviorMap) {
            this.behaviorMap = behaviorMap;
        }

        public void setDefault_p_total(long default_p_total) {
            this.default_p_total = default_p_total;
        }

        public void setDefault_p_avg(long default_p_avg) {
            this.default_p_avg = default_p_avg;
        }

        public void setDefault_u_total(long default_u_total) {
            this.default_u_total = default_u_total;
        }

        public void setDefault_u_avg(long default_u_avg) {
            this.default_u_avg = default_u_avg;
        }
    }
}