package com.xiang.xservice.application.script.xb.schedule.xb;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.xiang.xmc.service.cache.service.IRedisService;
import com.xiang.xmc.service.message.dingTalk.service.DingTalkService;
import com.xiang.xservice.application.script.xb.entity.pojo.xb.FundMessage;
import com.xiang.xservice.application.script.xb.entity.pojo.xb.XbFundList;
import com.xiang.xservice.application.script.xb.entity.response.xbyj.fund.FundInfo;
import com.xiang.xservice.application.script.xb.entity.response.xbyj.fund.FundList;
import com.xiang.xservice.application.script.xb.repository.XBFundMapper;
import com.xiang.xservice.application.script.xb.service.FundService;
import com.xiang.xservice.basic.config.MyThreadFactory;
import com.xiang.xservice.basic.utils.DateUtils;
import com.xiang.xservice.common.entity.DayResult;
import com.xiang.xservice.common.enums.RedisConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: xiang
 * @Date: 2025-05-14 17:15
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class FundInfoQueryJob {

    private final FundService fundService;
    private final DingTalkService dingTalkService;
    private final XBFundMapper xbFundMapper;
    private final IRedisService redisService;
    private final ExecutorService es =
            new ThreadPoolExecutor(
                    10,
                    20,
                    1000,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(),
                    new MyThreadFactory("xb-query-thread", Boolean.TRUE),
                    new ThreadPoolExecutor.AbortPolicy());
    @Value("${xiaobei.codeArr}")
    private String codeArr;
    @Value("${dingtalk.chatId}")
    private String chatId;

    /**
     * 基金涨跌幅5分钟超过2%重点通知
     */
    @Scheduled(cron = "0 0/5 9,10,11,13,14,15 * * ? ")
    public void queryFundEmergencyJob() throws Exception {
        // 周六周日过滤
        if (DateUtils.validWeekTime()) return;
        LocalDate now = LocalDate.now();
        com.alibaba.fastjson.JSONObject dayResult = (com.alibaba.fastjson.JSONObject) redisService.hGet(RedisConstant.DAY_INFO_PREFIX_KEY + RedisConstant.getDate4Key(), DateUtils.getDateFromDate(now));
        com.xiang.xservice.common.utils.DateUtils dateUtils = new com.xiang.xservice.common.utils.DateUtils(redisService);
        if (dateUtils.validHoliday(JSON.toJavaObject(dayResult, DayResult.class))) return;

        List<com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo> fundInfos = xbFundMapper.queryListIn2Min();
        if (CollectionUtils.isEmpty(fundInfos)) {
            return;
        }
        Map<String, List<com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo>> map = fundInfos.stream().collect(Collectors.groupingBy(com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo::getCode));
        StringBuffer sb = new StringBuffer();
        map.entrySet().parallelStream().forEach(entry -> {
            List<com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo> infos = entry.getValue();
            BigDecimal sum = new BigDecimal("0");
            for (int i = 0; i < infos.size() - 1; i++) {
                BigDecimal subtract = new BigDecimal(infos.get(i + 1).getChange()).subtract(new BigDecimal(infos.get(i).getChange()));
                sum = sum.add(subtract);
            }
            BigDecimal avg = sum.divide(new BigDecimal(String.valueOf(infos.size())), 2, RoundingMode.HALF_UP);
            if (avg.abs().compareTo(new BigDecimal("2")) > 0) {
                sb.append("基金").append(entry.getValue().get(0).getName()).append("5分钟的平均涨跌幅超过2%，涨跌幅达到").append(avg).append("\n");
            }
        });
        if (StringUtils.isNotBlank(sb)) {
            dingTalkService.sendChatMessage(chatId, sb.toString());
        }
    }

    /**
     * 基金每分钟涨幅记录
     */
    @Scheduled(cron = "0 0/1 9,10,11,13,14 * * ?")
    public void queryFundInfoInMinJob() {
        // 周六周日过滤
        if (Objects.equals(LocalDateTime.now().getDayOfWeek(), DayOfWeek.SATURDAY) ||
                Objects.equals(LocalDateTime.now().getDayOfWeek(), DayOfWeek.SUNDAY)) {
            return;
        }
        List<FundMessage> fundMessageList = queryFund(null);
        if (CollectionUtils.isEmpty(fundMessageList)) {
            return;
        }
        List<CompletableFuture> futures = Lists.newArrayList();
        List<com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo> fundInfoList = Lists.newCopyOnWriteArrayList();
        fundMessageList.parallelStream().forEach(fundMessage -> {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                FundInfo fundInfo = fundService.queryFundInfo(fundMessage.getCode());

                com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo info = com.xiang.xservice.application.script.xb.entity.pojo.xb.FundInfo.builder()
                        .code(fundMessage.getCode())
                        .name(fundInfo.getName())
                        .change(fundMessage.getChange().multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP).toString())
                        .updateTime(getTimeFromStr(fundMessage.getDate(), fundMessage.getUpdate()))
                        .build();
                fundInfoList.add(info);
            }, es);
            futures.add(future);
        });

        CompletableFuture[] futureArr = futures
                .toArray(futures.toArray(new CompletableFuture[0]));
        CompletableFuture.allOf(futureArr).join();

        if (CollectionUtils.isNotEmpty(fundInfoList)) {
            log.info("[基金查询] 每分钟基金涨跌幅查询记录，需要插入的数据:{}", JSONObject.toJSONString(fundInfoList));
            xbFundMapper.batchSave(fundInfoList);
        }
    }

    private List<FundMessage> queryFund(Integer type) {
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        List<String> code = Lists.newArrayList();
        List<XbFundList> lists = xbFundMapper.queryFundList(type);
        if (CollectionUtils.isEmpty(lists)) {
            if (Objects.equals(type, 1)) {
                code = Arrays.stream(codeArr.split(", ")).collect(Collectors.toList());
            }
            return Lists.newArrayList();
        } else {
            code = lists.stream().map(XbFundList::getCode).collect(Collectors.toList());
        }
        List<FundList> fundLists = fundService.queryTodayList(date, "2", Boolean.TRUE, code);
        if (CollectionUtils.isEmpty(fundLists)) {
            return Lists.newArrayList();
        }
        List<FundMessage> result = Lists.newCopyOnWriteArrayList();
        fundLists.parallelStream().forEach(fundList -> {
            FundInfo fundInfo = fundService.queryFundInfo(fundList.getCode());
            if (Objects.nonNull(fundInfo)) {
                FundMessage fund = FundMessage.builder().name(fundInfo.getName()).date(fundList.getDate())
                        .code(fundList.getCode()).change(fundList.getChange()).update(fundList.getUpdate()).build();
                result.add(fund);
            }
        });
        return result;
    }

    private LocalDateTime getTimeFromStr(String date, String time) {
        String dateTimeStr = date + " " + time;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return LocalDateTime.parse(dateTimeStr, formatter);
    }

}
