package com.lanyuanxiaoyao.leopard.core.task;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
import com.lanyuanxiaoyao.leopard.core.helper.NumberHelper;
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import com.lanyuanxiaoyao.leopard.core.service.TuShareService;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * 更新日线数据
 *
 * @author lanyuanxiaoyao
 * @version 20250924
 */
@Slf4j
@Component
public class UpdateDailyTask extends TaskRunner {
  private final StockRepository stockRepository;
  private final DailyRepository dailyRepository;

  private final TransactionTemplate transactionTemplate;

  private final TuShareService tuShareService;

  protected UpdateDailyTask(ApplicationContext context, StockRepository stockRepository, DailyRepository dailyRepository, TransactionTemplate transactionTemplate, TuShareService tuShareService) {
    super(context);
    this.stockRepository = stockRepository;
    this.dailyRepository = dailyRepository;
    this.transactionTemplate = transactionTemplate;
    this.tuShareService = tuShareService;
  }

  @Override
  public String process(Map<String, Object> params, StepUpdater updater) throws Exception {
    var tradeDates = new HashSet<LocalDate>();
    for (String exchange : List.of("SSE", "SZSE", "BSE")) {
      var response = tuShareService.tradeDateList(exchange);
      for (List<String> item : response.data().items()) {
        if (ObjectUtil.isNotEmpty(item) && StrUtil.isNotBlank(item.getFirst())) {
          tradeDates.add(LocalDate.parse(item.getFirst(), TuShareService.TRADE_FORMAT));
        }
      }
    }
    var existsTradeDates = dailyRepository.findDistinctTradeDate();
    var nowDate = LocalDate.now();
    var stocksMap = stockRepository.findAll().stream().collect(Collectors.toMap(Stock::getCode, stock -> stock));
    var targetTradeDates = tradeDates.stream()
      .filter(date -> date.isBefore(nowDate) || date.isEqual(nowDate))
      .filter(date -> !existsTradeDates.contains(date))
      .toList();
    var total = targetTradeDates.size();
    var finished = new AtomicInteger(0);
    targetTradeDates.parallelStream()
      .forEach(tradeDate -> {
        var factorResponse = tuShareService.factorList(tradeDate);
        var factorMap = new HashMap<String, Double>();
        for (List<String> item : factorResponse.data().items()) {
          factorMap.put(item.get(0), Double.valueOf(item.get(2)));
        }
        transactionTemplate.execute(status -> {
          var response = tuShareService.dailyList(tradeDate);
          var dailies = new ArrayList<Daily>();
          for (List<String> item : response.data().items()) {
            var code = item.get(0);
            if (stocksMap.containsKey(code)) {
              var stock = stocksMap.get(code);
              var factor = factorMap.get(code);
              var daily = new Daily();
              daily.setTradeDate(tradeDate);
              daily.setOpen(NumberHelper.parseDouble(item.get(2)));
              daily.setHigh(NumberUtil.parseDouble(item.get(3)));
              daily.setLow(NumberUtil.parseDouble(item.get(4)));
              daily.setClose(NumberUtil.parseDouble(item.get(5)));
              daily.setPreviousClose(NumberUtil.parseDouble(item.get(6)));
              daily.setPriceChangeAmount(NumberUtil.parseDouble(item.get(7)));
              daily.setPriceFluctuationRange(NumberUtil.parseDouble(item.get(8)));
              daily.setVolume(NumberUtil.parseDouble(item.get(9)));
              daily.setTurnover(NumberUtil.parseDouble(item.get(10)));
              daily.setFactor(factor);
              daily.setStock(stock);
              dailies.add(daily);
            }
          }
          dailyRepository.saveAll(dailies);
          return null;
        });
        updater.update(finished.incrementAndGet() * 1.0 / total);
      });
    return null;
  }
}
