package com.slodon.b2b2c.investment.model;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.slodon.b2b2c.core.response.PagerInfo;
import com.slodon.b2b2c.core.util.HttpClientUtil;
import com.slodon.b2b2c.investment.bean.analysis.bo.*;
import com.slodon.b2b2c.investment.bean.analysis.dto.CompanyDTO;
import com.slodon.b2b2c.investment.bean.analysis.dto.CompanyListDTO;
import com.slodon.b2b2c.investment.bean.analysis.dto.PdfDTO;
import com.slodon.b2b2c.investment.bean.analysis.dto.RivalDTO;
import com.slodon.b2b2c.investment.bean.analysis.vo.CompanyEntity;
import com.slodon.b2b2c.investment.bean.analysis.vo.KeyWordPartVO;
import com.slodon.b2b2c.investment.bean.analysis.vo.RivalPartVO;
import com.slodon.b2b2c.investment.bean.analysis.vo.SuggestionVO;
import com.slodon.b2b2c.investment.config.AnalysisConfig;
import com.slodon.b2b2c.investment.constant.InvestmentConst;
import com.slodon.b2b2c.investment.constant.LocalLanguageEnum;
import com.slodon.b2b2c.investment.mapper.BusinessAnalysisMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.nio.charset.StandardCharsets;
import javax.annotation.Resource;
import java.util.concurrent.*;


/**
 * @author sunshihao
 * @version 1.0
 * @description: 定量分析模型
 * @date 2025/8/5 11:24
 */
@Component
@Slf4j
public class AnalysisModel {


    @Resource
    private AnalysisConfig analysisConfig;

    @Resource
    private BusinessAnalysisMapper businessAnalysisMapper;




/***
 * @description:翻译产品名称
 * @param originalText 各语言名称
 * @return: java.lang.String
 * @author sunshihao
 * @date: 2025/8/5 13:52
 */
public List<String> translateProduct(String originalText) throws Exception {
    HashMap<String, String> headers = new HashMap<>();
    headers.put(InvestmentConst.CONTENT_TYPE, InvestmentConst.APPLICATION_JSON);
    JSONObject param = new JSONObject();
    param.set(InvestmentConst.TEXT, originalText);
    param.set(InvestmentConst.MODEL_NAME, analysisConfig.getModelName());
    String result;
    try {
        result = HttpClientUtil.httpPost(analysisConfig.getTranslateAddress() + "/api/translate", param.toString(), headers);
    } catch (Exception e) {
        log.error(e.getMessage(), "产品翻译接口出错");
        throw new Exception("产品翻译接口出错", e);
    }
    JSONObject jsonObject = JSONUtil.parseObj(result);
    String text = jsonObject.getByPath("translated_text", String.class);
    if (StringUtils.isEmpty(text)) {
        throw new Exception("翻译结果为空");
    }
    int start = text.indexOf("[");
    int end = text.lastIndexOf("]") + InvestmentConst.NUM_ONE;
    String jsonString = "";
    if (start != InvestmentConst.NEGATIVE_ONE && end != InvestmentConst.NEGATIVE_ONE && start < end) {
        jsonString = text.substring(start, end);
    } else {
        throw new Exception("未找到翻译结果");
    }
    return JSONUtil.parseArray(jsonString).toList(String.class);
}


public HashMap<String,String> getCredential(){
    String credentials = analysisConfig.getLoginUser() + ":" + analysisConfig.getPassword();
    // 进行Base64编码
    String base64Creds = Base64.getEncoder()
            .encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
    // 创建请求头Map
    HashMap<String, String> headers = new HashMap<>();
    headers.put(InvestmentConst.CONTENT_TYPE, InvestmentConst.APPLICATION_JSON);
    headers.put(InvestmentConst.AUTHORIZATION, "Basic " + base64Creds);
    return headers;
}


private ThreadPoolExecutor createThreadPool() {
    return new ThreadPoolExecutor(
            InvestmentConst.DEFAULT_RUN_THREAD_NUM,
            InvestmentConst.DEFAULT_RUN_THREAD_NUM,
            InvestmentConst.KEEP_ALIVE_TIME,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(InvestmentConst.WORK_QUEUE),
            new ThreadFactory() {
                private int count = InvestmentConst.NUM_ZERO;
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("dataForSeo-api-call-thread-" + count++);
                    thread.setDaemon(true);
                    return thread;
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy()
    );
}
    private void shutdownThreadPool(ThreadPoolExecutor executor) {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(InvestmentConst.NUM_TEN, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }



/***
 * @description: 接口1
 * @param header 请求头
 * @param keywords 关键词
 * @param locationName 目标市场
 * @return: com.slodon.b2b2c.investment.bean.vo.KeyWordPartVO
 * @author sunshihao
 * @date: 2025/8/7 11:54
 */
public KeyWordPartVO searchVolume(HashMap<String,String> header,String keywords,String locationName) throws Exception {
    KeyWordPartVO keyWordPartVO = new KeyWordPartVO();
    JSONArray payload = new JSONArray();
    JSONObject payloadItem = new JSONObject();
    JSONArray keywordsArray = new JSONArray();
    keywordsArray.add(keywords);
    payloadItem.set(InvestmentConst.KEYWORDS, keywordsArray);
    payloadItem.set(InvestmentConst.LANGUAGE_NAME, InvestmentConst.ENGLISH);
    payloadItem.set(InvestmentConst.LOCATION_NAME, locationName);
    payload.put(payloadItem);
    String post = HttpClientUtil.httpPost(analysisConfig.getSearchVolume(), payload.toString(),header);
    JSONObject postObject = JSONUtil.parseObj(post);
    Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
    if(tasksError==null||tasksError> InvestmentConst.NUM_ZERO){
        throw new RuntimeException("接口1查询关键词 按月返回搜索量/CPC search_volume 任务失败");
    }
    MonthlySearchesBO bo = postObject.getByPath("tasks[0].result[0].monthly_searches[0]", MonthlySearchesBO.class);
    Double competition = postObject.getByPath("tasks[0].result[0].competition",Double.class);
    Integer cp = competition==null?null:(int)Math.round(competition * InvestmentConst.NUM_ONE_HUNDRED);
    keyWordPartVO.setMonthlySearchesBO(bo);
    keyWordPartVO.setCompetition(cp);
    return keyWordPartVO;
}


/***
 * @description: 上方右侧推荐数据
 * @param:
 * @param header 请求头
 * @param keyword 关键词
 * @param locationName 地区
 * @return: com.slodon.b2b2c.investment.bean.analysis.vo.SuggestionVO
 * @author sunshihao
 * @date: 2025/8/7 15:49
 */
public SuggestionVO KeywordForSuggestions(HashMap<String,String> header, String keyword, String locationName) throws Exception {
    SuggestionVO suggestionVO = new SuggestionVO();
    JSONArray payload = new JSONArray();
    JSONObject payloadItem = new JSONObject();
    payloadItem.set(InvestmentConst.KEYWORD, keyword);
    String languageName = LocalLanguageEnum.getLanguageNameByLocationName(locationName);
    payloadItem.set(InvestmentConst.LANGUAGE_NAME, languageName);
    payloadItem.set(InvestmentConst.LOCATION_NAME, locationName);
    payloadItem.set("include_serp_info", true);
    payloadItem.set("include_seed_keyword", true);
    payloadItem.set(InvestmentConst.LIMIT, InvestmentConst.NUM_TEN);
    payload.put(payloadItem);
    String post = HttpClientUtil.httpPost(analysisConfig.getKeywordForSuggestions(), payload.toString(),header);
    JSONObject postObject = JSONUtil.parseObj(post);
    Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
    if(tasksError==null||tasksError> InvestmentConst.NUM_ZERO){
        throw new RuntimeException("接口5查询推荐数据 任务失败");
    }
    JSONArray jsonArray = postObject.getByPath("tasks[0].result[0].items", JSONArray.class);
    if(jsonArray==null|| jsonArray.isEmpty()){
        return suggestionVO;
    }
    List<RelatedInfoBO> relatedInfoBOList = new ArrayList<>();
    for (Object o : jsonArray) {
        RelatedInfoBO bo = new RelatedInfoBO();
        JSONObject obj = (JSONObject) o;
        bo.setKeyword(obj.getByPath(InvestmentConst.KEYWORD, String.class));
        bo.setSearchVolume(obj.getByPath("keyword_info.search_volume", Long.class));
        Double competition = obj.getByPath("keyword_info.competition", Double.class);
        bo.setCompetition(competition==null?null:(int)Math.round(competition * InvestmentConst.NUM_ONE_HUNDRED));
        relatedInfoBOList.add(bo);
    }
    suggestionVO.setRelatedInfoBOList(relatedInfoBOList);
    return suggestionVO;
}




    public RivalPartVO rival(HashMap<String, String> header, RivalDTO dto) throws Exception {
        String websites = dto.getCompetitorWebsite();
        String[] competitorWebsites = websites.split(",");
        RivalPartVO vo = new RivalPartVO();
        if(competitorWebsites.length== InvestmentConst.NUM_ZERO){
            throw new Exception("竞品网站地址不正确");
        }
        List<CompetitorBO> competitorBOS = new ArrayList<>();
        ThreadPoolExecutor executor = createThreadPool();
        try {
            for (String competitorWebsite : competitorWebsites) {
                CompetitorBO bo = new CompetitorBO();
                bo.setWebsite(competitorWebsite);
                // 推荐投放关键词表格（接口3）
                CompletableFuture<List<RecommendationBO>> recommendationBOListFuture = CompletableFuture.supplyAsync(
                        () -> {
                            try {
                                return keywordsForSite(header, competitorWebsite, dto.getLocationName());
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        },
                        executor
                );
                // 折线图表格（接口8）
                CompletableFuture<List<TrafficBO>> trafficBOListFuture = CompletableFuture.supplyAsync(
                        () -> {
                            try {
                                return historicalTraffic(header, competitorWebsite, dto.getLocationName());
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        },
                        executor
                );
                // 排名表格（接口7）
                CompletableFuture<List<RankBO>> rankBOListFuture = CompletableFuture.supplyAsync(
                        () -> {
                            try {
                                return rankedKeywords(header, competitorWebsite, dto.getLocationName());
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        },
                        executor
                );
                CompletableFuture.allOf(recommendationBOListFuture, trafficBOListFuture,rankBOListFuture).join();
                bo.setRecommendationBOList(recommendationBOListFuture.get());
                bo.setTrafficBOList(trafficBOListFuture.join());
                bo.setRankBOList(rankBOListFuture.get());
                competitorBOS.add(bo);
            }
        }finally {
            shutdownThreadPool(executor);
        }
        vo.setCompetitorBOS(competitorBOS);
        return vo;
    }


    // 接口7
    public List<RankBO> rankedKeywords(HashMap<String,String> header,String website,String locationName) throws Exception {
        JSONArray payload = new JSONArray();
        JSONObject payloadItem = new JSONObject();
        payloadItem.set(InvestmentConst.TARGET, website);
        String languageName = LocalLanguageEnum.getLanguageNameByLocationName(locationName);
        payloadItem.set(InvestmentConst.LANGUAGE_NAME, languageName);
        payloadItem.set(InvestmentConst.LOCATION_NAME, locationName);
        payload.put(payloadItem);
        String post = HttpClientUtil.httpPost(analysisConfig.getRankedKeywords(), payload.toString(),header);
        JSONObject postObject = JSONUtil.parseObj(post);
        Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
        if(tasksError==null||tasksError> InvestmentConst.NUM_ZERO){
            throw new RuntimeException("接口7查询 关键词排名 任务失败");
        }
        JSONArray items = postObject.getByPath("tasks[0].result[0].items", JSONArray.class);
        List<RankBO> list = new ArrayList<>();
        if(items==null|| items.isEmpty()){
            return list;
        }
        for (Object item : items) {
            JSONObject obj = (JSONObject) item;
            String keyword = obj.getByPath("keyword_data.keyword", String.class);
            Long searchVolume = obj.getByPath("keyword_data.keyword_info.search_volume", Long.class);
            Double competition = obj.getByPath("keyword_data.keyword_info.competition", Double.class);
            Integer dp = competition==null?null:(int)Math.round(competition * InvestmentConst.NUM_ONE_HUNDRED);
            Double cpc = obj.getByPath("keyword_data.keyword_info.cpc", Double.class);
            Double cpcFormat=cpc==null?null:Math.round(cpc * InvestmentConst.NUM_ONE_HUNDRED) / InvestmentConst.ONE_HUNDRED_DOUBLE;
            Long monthly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.monthly", Long.class);
            Long quarterly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.quarterly", Long.class);
            Long yearly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.yearly", Long.class);
            RankBO bo = RankBO.builder()
                    .keyword(keyword)
                    .searchVolume(searchVolume)
                    .monthly(monthly)
                    .quarterly(quarterly)
                    .yearly(yearly)
                    .dp(dp)
                    .cpc(cpcFormat)
                    .build();
            list.add(bo);
        }
        return list;
    }

    // 接口8
    public List<TrafficBO> historicalTraffic(HashMap<String,String> header,String website,String locationName) throws Exception {
        JSONArray payload = new JSONArray();
        JSONObject payloadItem = new JSONObject();
        JSONArray targets = new JSONArray();
        String keyword = StringUtils.removeStart(
            StringUtils.removeStart(
                StringUtils.removeStart(website, InvestmentConst.HTTPS),
                    InvestmentConst.HTTP),
                InvestmentConst.WWW);
        int comIndex = keyword.indexOf(InvestmentConst.COM);
        if (comIndex != -InvestmentConst.NEGATIVE_ONE) {
            keyword =  keyword.substring(InvestmentConst.NUM_ZERO, comIndex+ InvestmentConst.NUM_FOUR);
        }
        targets.add(keyword);
        payloadItem.set(InvestmentConst.TARGETS,targets);
        String languageName = LocalLanguageEnum.getLanguageNameByLocationName(locationName);
        payloadItem.set(InvestmentConst.LANGUAGE_NAME, languageName);
        payloadItem.set(InvestmentConst.LOCATION_NAME, locationName);
        payload.put(payloadItem);
        LocalDate currentDate = LocalDate.now();
        LocalDate currentMonthFirstDay = currentDate.withDayOfMonth(InvestmentConst.NUM_ONE);
        LocalDate lastYearSameMonthFirstDay = currentDate.minusYears(InvestmentConst.NUM_ONE).plusMonths(InvestmentConst.NUM_ONE).withDayOfMonth(InvestmentConst.NUM_ONE);
        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
        payloadItem.set("date_from", lastYearSameMonthFirstDay.format(formatter));
        payloadItem.set("date_to", currentMonthFirstDay.format(formatter));
        JSONArray itemTypes = new JSONArray();
        itemTypes.put("organic");
        itemTypes.put("paid");
        payloadItem.set("item_types", itemTypes);
        JSONObject postData = new JSONObject();
        postData.set("0", payloadItem);

        String post = HttpClientUtil.httpPost(analysisConfig.getHistoricalTraffic(), postData.toString(),header);
        JSONObject postObject = JSONUtil.parseObj(post);
        Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
        if(tasksError==null||tasksError> InvestmentConst.NUM_ZERO){
            throw new RuntimeException("接口8查询 历史流量 任务失败");
        }
        List<TrafficBO> list = new ArrayList<>();
        JSONArray organics = postObject.getByPath("tasks[0].result[0].items[0].metrics.organic", JSONArray.class);
        JSONArray paids = postObject.getByPath("tasks[0].result[0].items[0].metrics.paid", JSONArray.class);
        Map<String, Long> temp = new HashMap<>();
        if(organics!=null&&!organics.isEmpty()){
            for (Object organic : organics) {
                JSONObject obj = (JSONObject) organic;
                int month = obj.getByPath("month", Integer.class);
                int year = obj.getByPath("year", Integer.class);
                long organicCount = obj.getByPath("count", Long.class);
                temp.put(year+"-"+month,organicCount);
            }
        }
        if(paids!=null&&!paids.isEmpty()){
            for (Object paid : paids) {
                TrafficBO bo = new TrafficBO();
                JSONObject obj = (JSONObject) paid;
                int month = obj.getByPath("month", Integer.class);
                int year = obj.getByPath("year", Integer.class);
                long paidCount = obj.getByPath("count", Long.class);
                long organicCount = temp.get(year+"-"+month);
                bo.setX_axis(year+"-"+month);
                bo.setPaid(paidCount);
                bo.setOrganic(organicCount);
                list.add(bo);
            }
        }
        Collections.reverse(list);
        return list;
    }

    // 接口3
    public List<RecommendationBO> keywordsForSite(HashMap<String, String> header, String website, String locationName) throws Exception {
        JSONArray payload = new JSONArray();
        JSONObject payloadItem = new JSONObject();
        payloadItem.set(InvestmentConst.TARGET, website);
        payloadItem.set(InvestmentConst.LANGUAGE_NAME, InvestmentConst.ENGLISH);
        payloadItem.set(InvestmentConst.LOCATION_NAME, locationName);
        payloadItem.set(InvestmentConst.LIMIT, InvestmentConst.NUM_FIFTY);
        payload.put(payloadItem);
        String post = HttpClientUtil.httpPost(analysisConfig.getKeywordsForSite(), payload.toString(),header);
        JSONObject postObject = JSONUtil.parseObj(post);
        Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
        if(tasksError==null||tasksError> InvestmentConst.NUM_ZERO){
            throw new RuntimeException("接口3查询 keywordsForSite 任务失败");
        }
        List<RecommendationBO> list = new ArrayList<>();
        JSONArray results = postObject.getByPath("tasks[0].result", JSONArray.class);
        if(results==null){
            return list;
        }
        int total = Math.min(results.size(), InvestmentConst.NUM_FIFTY);
        for(int i = InvestmentConst.NUM_ZERO; i<total; i++){
            JSONObject result = (JSONObject) results.get(i);
            RecommendationBO bo = new RecommendationBO();
            bo.setKeywords(result.getByPath(InvestmentConst.KEYWORD,String.class));
            bo.setSearchVolume(result.getByPath("search_volume",Long.class));
            Double cpc = result.getByPath("cpc",Double.class);
            Double cpcFormat=cpc==null?null:Math.round(cpc * InvestmentConst.NUM_ONE_HUNDRED) / InvestmentConst.ONE_HUNDRED_DOUBLE;
            bo.setPrice(cpcFormat);
            list.add(bo);
        }
        return list;
    }

    public String qualitative(String keyword) throws Exception {
        HashMap<String, String> headers = new HashMap<>();
        headers.put(InvestmentConst.CONTENT_TYPE, InvestmentConst.APPLICATION_JSON);
        JSONObject param = new JSONObject();
        param.set("question", keyword);
        param.set("initial_search_query_count", InvestmentConst.NUM_THREE);
        param.set("max_research_loops", InvestmentConst.NUM_THREE);
        param.set("reasoning_model", analysisConfig.getModelName());
        String result;
        try {
            result = HttpClientUtil.httpPost(analysisConfig.getTranslateAddress() + "/api/research", param.toString(), headers);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            String answer = jsonObject.getByPath("answer",String.class);
            if(StringUtils.isEmpty(answer)){
                return "";
            }
        return dealAnswer(answer);
        } catch (Exception e) {
            log.error("定性分析接口出错", e);
            throw new Exception("定性分析接口出错", e);
        }
    }

    private String dealAnswer(String answer) {
        answer = answer.replace(InvestmentConst.CHINESE, "");
        String lastPart = getLastPart(answer);
        String firstPart = getFirstPart(answer);
        return firstPart + lastPart;
    }

    private String getLastPart(String answer) {
        int startIndex = answer.indexOf (InvestmentConst.LAST_PART_MARKER);
        if (startIndex != InvestmentConst.NEGATIVE_ONE) {
            return answer.substring (startIndex);
        } else {
            return "";
        }
    }

    public String getFirstPart (String input) {
        int markerIndex = input.indexOf (InvestmentConst.FIRST_MARKER);
        if (markerIndex != InvestmentConst.NEGATIVE_ONE) {
            return input.substring (InvestmentConst.NUM_ZERO, markerIndex);
        } else {
            return input;
        }
    }

    public int record(CompanyDTO dto) throws Exception {
        // 查询是否达到3次
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMM");
        Date date = new Date();
        LocalDateTime currentTime = date
                .toInstant()
                .atZone(ZoneId.of("Asia/Shanghai"))
                .toLocalDateTime();
        String month = currentTime.format(format);
        CompanyBo bo = new CompanyBo();
        bo.setPhone(dto.getPhone());
        bo.setQueryMonth(month);
        int count = businessAnalysisMapper.countQueryTimes(bo);
        if(count>=InvestmentConst.NUM_THREE){
            throw new RuntimeException("该手机号已超过3次查询");
        }
        // 插入记录数据
        CompanyEntity entity = CompanyEntity.builder()
                .companyName(dto.getCompanyName())
                .phone(dto.getPhone())
                .createTime(currentTime)
                .queryMonth(month)
                .usedCount(InvestmentConst.NUM_ONE+count)
                .leftCount(InvestmentConst.NUM_TWO-count)
                .build();
        int res = businessAnalysisMapper.insertRecord(entity);
        if(InvestmentConst.NUM_ONE!=res){
            throw new Exception("ai商情分析记录失败");
        }
        return entity.getId();
    }

    public void savePdf(PdfDTO dto) throws Exception {
        int res = businessAnalysisMapper.updateRecord(dto);
        if(InvestmentConst.NUM_ONE!=res){
            throw new Exception("ai商情分析pdf地址更新失败");
        }
    }

    public List<CompanyEntity> aiList(CompanyListDTO dto, PagerInfo pager) {
        List<CompanyEntity> list = Collections.emptyList();
        if (pager != null) {
            pager.setRowsCount(businessAnalysisMapper.countAiList(dto));
            list = businessAnalysisMapper.aiList(dto, pager.getStart(), pager.getPageSize());
        }
        return list;
    }


}



