package com.lanyuanxiaoyao.leopard.server.service.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.repository.DailyRepository;
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import com.lanyuanxiaoyao.leopard.server.helper.NumberHelper;
import com.lanyuanxiaoyao.leopard.server.service.TuShareService;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionTemplate;

@Slf4j
@LiteflowComponent("update_daily")
public class UpdateDailyNode extends NodeComponent {
  private final StockRepository stockRepository;
  private final DailyRepository dailyRepository;

  private final TuShareService tuShareService;

  private final TransactionTemplate transactionTemplate;

  public UpdateDailyNode(StockRepository stockRepository, DailyRepository dailyRepository, TuShareService tuShareService, TransactionTemplate transactionTemplate) {
    this.stockRepository = stockRepository;
    this.dailyRepository = dailyRepository;
    this.tuShareService = tuShareService;
    this.transactionTemplate = transactionTemplate;
  }

  @Override
  public void process() {
    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));
    tradeDates.parallelStream()
      .filter(date -> date.isBefore(nowDate) || date.isEqual(nowDate))
      .filter(date -> !existsTradeDates.contains(date))
      // .filter(date -> date.isAfter(LocalDate.of(2024, 12, 31)))
      .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)));
        }

        var response = tuShareService.dailyList(tradeDate);
        transactionTemplate.execute(status -> {
          try {
            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);
                dailyRepository.save(daily);
              }
            }
            return true;
          } catch (Exception exception) {
            log.error("Error", exception);
            status.setRollbackOnly();
            return false;
          }
        });
      });
  }
}
