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

import com.alibaba.fastjson.JSON;
import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import com.google.common.collect.Sets;
import com.tzld.piaoquan.recommend.server.common.enums.TabSource;
import com.tzld.piaoquan.recommend.server.common.enums.TabType;
import com.tzld.piaoquan.recommend.server.gen.common.Result;
import com.tzld.piaoquan.recommend.server.gen.common.TabProto;
import com.tzld.piaoquan.recommend.server.gen.tab.TabRequest;
import com.tzld.piaoquan.recommend.server.gen.tab.TabResponse;
import com.tzld.piaoquan.recommend.server.model.RecommendParam;
import com.tzld.piaoquan.recommend.server.model.TabParam;
import com.tzld.piaoquan.recommend.server.model.Video;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TabService {


    @Autowired
    @Qualifier("redisTemplate")
    protected RedisTemplate<String, String> redisTemplate;

    @ApolloJsonValue("${homepage.default.tab:[历史名人,知识科普]}")
    private List<String> homepageDefaultTab;

    @ApolloJsonValue("${personalization.tab.size:2}")
    private Integer personalizationTabSize;

    /**
     * 中国行政区划代码（GB/T 2260）省級前缀，前2位。
     * 11-15 华北, 21-23 东北, 31-37 华东, 41-46 中南, 50-54 西南, 61-65 西北,
     * 71 台湾, 81-82 香港/澳门
     */
    private static final Set<String> CHINA_PROVINCE_CODE_PREFIXES = Sets.newHashSet(
            "11", "12", "13", "14", "15",
            "21", "22", "23",
            "31", "32", "33", "34", "35", "36", "37",
            "41", "42", "43", "44", "45", "46",
            "50", "51", "52", "53", "54",
            "61", "62", "63", "64", "65",
            "71", "81", "82"
    );

    private static final String personalizationTabCondidateFormat = "personalized_tab_candidate:%s";

    private static final String userInvalidTabCache = "user:invalid:tab:cache:%s";

    private static final String PERSONALIZATION_TAB_SEPARATOR = "&";

    public TabResponse homepageTab(TabRequest request) {
        TabParam tabParam = this.requestCoverToParam(request);

        List<TabProto> tabProtos = new ArrayList<>();
        Set<String> tabs = new HashSet<>();

        List<TabProto> personalizationTabs = this.personalizationTab(tabParam, tabs);
        tabProtos.addAll(this.territoryTab(tabParam, tabs));
        tabProtos.addAll(personalizationTabs);

        return TabResponse.newBuilder()
                .setResult(Result.newBuilder().setCode(1).setMessage("success"))
                .addAllTab(tabProtos)
                .build();
    }

    private List<TabProto> territoryTab(TabParam tabParam, Set<String> tabs) {
        List<TabProto> tabProtos = new ArrayList<>();
        // 非中国地域不返回地域Tab
        if (isChinaRegion(tabParam.getProvinceCode())) {
            String province = tabParam.getProvince().replaceAll("省$", "");
            List<TabProto> provinceTab = this.buildTabProto(Collections.singletonList(province), TabSource.other, TabType.territory, tabs);
            tabProtos.addAll(provinceTab);
        }
        return tabProtos;
    }

    private List<TabProto> personalizationTab(TabParam tabParam, Set<String> tabs) {

        List<TabProto> defaultTab = this.buildTabProto(homepageDefaultTab, TabSource.default_hot, TabType.personalization, tabs);
        List<TabProto> tabProtos = new ArrayList<>(defaultTab);

        if (StringUtils.isBlank(tabParam.getUnionId())) {
            log.error("unionId is empty");
            return tabProtos;
        }

        String key = String.format(personalizationTabCondidateFormat, tabParam.getUnionId());
        String value = redisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(value)) {
            log.error("unionId is not exist");
            return tabProtos;
        }

        List<List<String>> tabCollect = Arrays.stream(value.split("\t"))
                .map(i -> Arrays.asList(i.split(PERSONALIZATION_TAB_SEPARATOR)))
                .collect(Collectors.toList());
        if (tabCollect.size() != 4) {
            log.error("redis value size nq 4, {}", value);
            return tabProtos;
        }

        log.info("tabCollect: {}", JSON.toJSONString(tabCollect));

        List<TabProto> profileTab = this.buildTabProto(tabCollect.get(0), tabParam.getUnionId(), TabSource.profile, TabType.personalization, tabs);
        List<TabProto> shareCate2Tab = this.buildTabProto(tabCollect.get(1), tabParam.getUnionId(), TabSource.share_cate2, TabType.personalization, tabs);
        List<TabProto> returnCate2Tab = this.buildTabProto(tabCollect.get(2), tabParam.getUnionId(), TabSource.return_cate2, TabType.personalization, tabs);
        List<TabProto> defaultHotTab = this.buildTabProto(tabCollect.get(3), tabParam.getUnionId(), TabSource.default_hot, TabType.personalization, tabs);

        // 默认兜底Tab
        tabProtos.addAll(0, defaultHotTab);
        tabProtos.addAll(0, returnCate2Tab);
        tabProtos.addAll(0, shareCate2Tab);
        tabProtos.addAll(0, profileTab);
        return tabProtos;
    }

    private List<TabProto> buildTabProto(List<String> tabNames, String unionId, TabSource source, TabType type, Set<String> tabs) {
        if (CollectionUtils.isEmpty(tabNames)) {
            return Collections.emptyList();
        }


        String invalidTabCacheKey = String.format(userInvalidTabCache, unionId);
        Set<String> invalidTabCache = Optional.ofNullable(redisTemplate.opsForSet().members(invalidTabCacheKey)).orElse(new HashSet<>());

        List<TabProto> tabProtos = new ArrayList<>();
        for (String tabName : tabNames) {
            if (StringUtils.isBlank(tabName)
                    || tabs.contains(tabName)
                    || invalidTabCache.contains(tabName)) {
                continue;
            }
            tabProtos.add(
                    TabProto.newBuilder()
                            .setKey(tabName)
                            .setTitle(tabName)
                            .setSource(source.getSource())
                            .setType(type.getType())
                            .build()
            );
            tabs.add(tabName);
        }
        return tabProtos;
    }

    private TabParam requestCoverToParam(TabRequest request) {
        TabParam param = new TabParam();
        param.setRequestId(request.getRequestId());

        param.setMid(request.getMid());
        param.setUid(request.getUid());
        param.setUnionId(request.getUnionId());

        param.setAppType(request.getAppType());

        param.setCityCode(request.getCityCode());
        param.setProvinceCode(request.getProvinceCode());
        param.setCity(request.getCity());
        param.setProvince(request.getProvince());

        if (request.getAbExpCodeCount() > 0) {
            Set<String> abExpCodes = new HashSet<>(request.getAbExpCodeList());
            param.setAbExpCodes(abExpCodes);
        }
        param.setRootSessionId(request.getRootSessionId());

        return param;
    }

    /**
     * 判断行政区划代码是否属于中国。
     * 通过省份代码前2位匹配 GB/T 2260 中的中国省级前缀来校验。
     *
     * @param provinceCode 省份代码（2位或6位均可）
     * @return true 表示是中国地域
     */
    private boolean isChinaRegion(String provinceCode) {
        if (StringUtils.isBlank(provinceCode) || provinceCode.length() < 2) {
            return false;
        }
        return CHINA_PROVINCE_CODE_PREFIXES.contains(provinceCode.substring(0, 2));
    }

    public void updateUserTabCache(RecommendParam param, List<Video> videos) {
        try {

            if (Objects.isNull(param.getTab()) || StringUtils.isBlank(param.getTab().getKey())) {
                return;
            }
            int size = param.getSize();
            if (CollectionUtils.isEmpty(videos) || videos.size() < size) {
                String key = String.format(userInvalidTabCache, param.getUnionId());
                redisTemplate.opsForSet().add(key, param.getTab().getKey());
                redisTemplate.expire(key, 360 * 3600, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            log.error("updateUserTabCache error", e);
        }
    }

}
