package com.tzld.longarticle.recommend.server.service.recall;

import com.tzld.longarticle.recommend.server.common.ThreadPoolFactory;
import com.tzld.longarticle.recommend.server.model.Content;
import com.tzld.longarticle.recommend.server.service.recall.strategy.DefaultRecallStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author dyp
 */
@Service
@Slf4j
public class RecallService implements ApplicationContextAware {

    private final Map<String, RecallStrategy> strategyMap = new HashMap<>();
    private ApplicationContext applicationContext;
    private final ExecutorService pool = ThreadPoolFactory.recallPool();

    @PostConstruct
    public void init() {
        Map<String, RecallStrategy> type = applicationContext.getBeansOfType(RecallStrategy.class);
        for (Map.Entry<String, RecallStrategy> entry : type.entrySet()) {
            RecallStrategy value = entry.getValue();
            strategyMap.put(value.getClass().getSimpleName(), value);
        }
    }

    public RecallResult recall(RecallParam param) {
        List<RecallStrategy> strategies = getRecallStrategy(param);
        CountDownLatch cdl = new CountDownLatch(strategies.size());
        List<Future<RecallResult.RecallData>> recallResultFutures = new ArrayList<>();
        for (final RecallStrategy strategy : strategies) {
            Future<RecallResult.RecallData> future = pool.submit(() -> {
                List<Content> result = strategy.recall(param);
                cdl.countDown();
                return new RecallResult.RecallData(result);
            });
            recallResultFutures.add(future);
        }
        try {
            cdl.await(3000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("recall error", e);
            return null;
        }

        List<RecallResult.RecallData> results = new ArrayList<>();
        for (Future<RecallResult.RecallData> f : recallResultFutures) {
            try {
                RecallResult.RecallData data = f.get();
                results.add(data);
            } catch (Exception e) {
                log.error("future get error ", e);
            }
        }

        return new RecallResult(results);
    }

    private List<RecallStrategy> getRecallStrategy(RecallParam param) {
        List<RecallStrategy> strategies = new ArrayList<>();
        strategies.add(strategyMap.get(DefaultRecallStrategy.class.getSimpleName()));

        return strategies;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}
