package com.tzld.piaoquan.recommend.server.service.filter;

import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import com.google.common.collect.Lists;
import com.tzld.piaoquan.recommend.server.common.ThreadPoolFactory;
import com.tzld.piaoquan.recommend.server.common.enums.RootSessionIdLayer;
import com.tzld.piaoquan.recommend.server.service.ExperimentService;
import com.tzld.piaoquan.recommend.server.service.ServiceBeanFactory;
import com.tzld.piaoquan.recommend.server.service.filter.strategy.*;
import com.tzld.piaoquan.recommend.server.service.funnel.RecallVideoEntry;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author dyp
 */
@Slf4j
@Component
public class FilterService {

    @Autowired
    private ExperimentService experimentService;

    private final ExecutorService pool = ThreadPoolFactory.filterPool();

    @ApolloJsonValue("${filter_exp_config:{}}")
    private Map<String, Integer> filterExpConfig;

    public FilterResult filter(FilterParam param) {
        // 阶段 1: filter 前 entries（按 funnel 开关创建并挂到 context）
        List<RecallVideoEntry> entries = recordRecallStageToContext(param);
        // 阶段 2: 跑 filter 策略，entries 非空时同步回写 filteredIn/indexNewAfterFilter/filterReasons
        List<Long> videoIds = viewFilter(param, entries);
        return new FilterResult(videoIds);
    }

    private List<RecallVideoEntry> recordRecallStageToContext(FilterParam param) {
        if (param == null
                || param.getFunnelContext() == null
                || StringUtils.isBlank(param.getPushFrom())
                || CollectionUtils.isEmpty(param.getVideoIds())) {
            return null;
        }
        List<Long> ids = param.getVideoIds();
        Map<Long, Double> scoresMap = param.getScoresMap();
        List<RecallVideoEntry> entries = new ArrayList<>(ids.size());
        for (int i = 0; i < ids.size(); i++) {
            long id = ids.get(i);
            double score = scoresMap != null ? scoresMap.getOrDefault(id, 0.0) : 0.0;
            entries.add(new RecallVideoEntry(id, score, i));
        }
        param.getFunnelContext().getStages123RecallByStrategy().merge(param.getPushFrom(), entries, (oldList, newList) -> {
            oldList.addAll(newList);
            return oldList;
        });
        return entries;
    }

    private List<Long> viewFilter(FilterParam param, List<RecallVideoEntry> entries) {

        List<FilterStrategy> strategies = getStrategies(param);
        CountDownLatch cdl = new CountDownLatch(strategies.size());
        List<Future<List<Long>>> futures = new ArrayList<>();
        for (final FilterStrategy strategy : strategies) {
            Future<List<Long>> future = pool.submit(() -> {
                List<Long> result = strategy.filter(param);
                cdl.countDown();
                return result;
            });
            futures.add(future);
        }
        try {
            cdl.await(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("filter error", e);
            Thread.currentThread().interrupt();
            return Collections.emptyList();
        }

        int n = strategies.size();
        List<Set<Long>> keeps = new ArrayList<>(n);
        List<String> reasonKeys = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            try {
                keeps.add(new HashSet<>(futures.get(i).get()));
            } catch (Exception e) {
                log.error("future get error ", e);
                keeps.add(Collections.emptySet());
            }
            reasonKeys.add(reasonKey(strategies.get(i)));
        }
        if (keeps.isEmpty()) {
            return Collections.emptyList();
        }

        // 漏斗未开 → 旧 retainAll 行为
        if (entries == null) {
            List<Long> result = Lists.newArrayList(param.getVideoIds());
            for (Set<Long> keep : keeps) {
                result.retainAll(keep);
            }
            return result;
        }

        // 漏斗开了 → 同步回写 entry 阶段 2 字段
        List<Long> result = new ArrayList<>(entries.size());
        int newPos = 0;
        for (RecallVideoEntry e : entries) {
            long id = e.getVideoId();
            List<String> reasons = null;
            for (int i = 0; i < keeps.size(); i++) {
                if (!keeps.get(i).contains(id)) {
                    if (reasons == null) reasons = new ArrayList<>(2);
                    reasons.add(reasonKeys.get(i));
                }
            }
            if (reasons == null) {
                e.setFilteredIn(true);
                e.setIndexNewAfterFilter(newPos++);
                result.add(id);
            } else {
                e.setFilteredIn(false);
                e.setFilterReasons(reasons);
            }
        }
        return result;
    }

    private static String reasonKey(FilterStrategy s) {
        if (s instanceof PreViewedStrategy) return "previewed";
        if (s instanceof ViewedStrategy) return "viewed";
        if (s instanceof RecommendStatusStrategy) return "recommend_status";
        if (s instanceof AppletVideoStatusStrategy) return "applet_video_status";
        if (s instanceof RiskVideoStrategy) return "risk";
        if (s instanceof VideoSourceTypeStrategy) return "ugc";
        if (s instanceof GeneralSpiderStrategy) return "spider";
        return s.getClass().getSimpleName();
    }

    private List<FilterStrategy> getStrategies(FilterParam param) {
        List<FilterStrategy> strategies = new ArrayList<>();
        strategies.add(ServiceBeanFactory.getBean(PreViewedStrategy.class));
        strategies.add(ServiceBeanFactory.getBean(ViewedStrategy.class));
        strategies.add(ServiceBeanFactory.getBean(RecommendStatusStrategy.class));
        strategies.add(ServiceBeanFactory.getBean(AppletVideoStatusStrategy.class));
        strategies.add(ServiceBeanFactory.getBean(RiskVideoStrategy.class));

        // UGC视频过滤
        Integer ugcFilterWeight = filterExpConfig.get("userupload");
        if (ugcFilterWeight != null && ugcFilterWeight != 0) {
            int rand = RandomUtils.nextInt(0, 100);
            if (ugcFilterWeight >= 100 || rand < ugcFilterWeight) {
                // log.info("hit ugc filter weight:{} rand:{}", ugcFilterWeight, rand);
                strategies.add(ServiceBeanFactory.getBean(VideoSourceTypeStrategy.class));
            }
        }

        // 全面spider视频过滤
        Integer allSpiderFilterWeight = filterExpConfig.get("allspider");
        if (allSpiderFilterWeight != null && allSpiderFilterWeight != 0) {
            int rand = RandomUtils.nextInt(0, 100);
            if (allSpiderFilterWeight >= 100 || rand < allSpiderFilterWeight) {
                // log.info("hit all spider filter weight:{} rand:{}", allSpiderFilterWeight, rand);
                strategies.add(ServiceBeanFactory.getBean(GeneralSpiderStrategy.class));
            }
        }

        boolean hit860Exp = experimentService.judgeHitExp(RootSessionIdLayer.product, param.getAppType(), param.getRootSessionId(), param.getAbExpCodes(), "860");
        if (hit860Exp) {
            strategies.remove(ServiceBeanFactory.getBean(PreViewedStrategy.class));
            strategies.add(ServiceBeanFactory.getBean(SessionPreViewedStrategy.class));
        }

//        if (CollectionUtils.isNotEmpty(param.getAbExpCodes()) && param.getAbExpCodes().contains("697")) {
//            strategies.add(ServiceBeanFactory.getBean(VideoSourceTypeStrategy.class));
//        }
//        //全面spider视频过滤
//        if (CollectionUtils.isNotEmpty(param.getAbExpCodes()) && param.getAbExpCodes().contains("722")) {
//            strategies.add(ServiceBeanFactory.getBean(GeneralSpiderStrategy.class));
//        }

        return strategies;
    }

}
