package com.lanyuanxiaoyao.leopard.core.service;

import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
import com.lanyuanxiaoyao.leopard.core.entity.QDaily;
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.springframework.stereotype.Service;

/**
 * 股票评估
 *
 * @author lanyuanxiaoyao
 * @version 20250924
 */
@Slf4j
@Service
public class AssessmentService {
  private final IndustryService industryService;
  private final DailyRepository dailyRepository;

  public AssessmentService(IndustryService industryService, DailyRepository dailyRepository) {
    this.industryService = industryService;
    this.dailyRepository = dailyRepository;
  }

  public Set<Result> assess(Set<Stock> stocks, int year) {
    if (ObjectUtil.isNotEmpty(stocks)) {
      var topChange = industryService.topChange(year, stocks);
      var dailyMap = dailyRepository.findAll(
          QDaily.daily.tradeDate.year().eq(year)
            .and(QDaily.daily.stock.in(stocks))
        )
        .stream()
        .collect(Collectors.groupingBy(Daily::getStock));
      return stocks
        .stream()
        .filter(stock -> {
          if (!dailyMap.containsKey(stock) || ObjectUtil.isEmpty(dailyMap.get(stock))) {
            log.warn("Cannot find daily data in {} for {}", year, stock.getCode());
            return false;
          }
          return true;
        })
        .map(stock -> {
          var dailies = dailyMap.get(stock)
            .stream()
            .sorted(Comparator.comparing(Daily::getTradeDate))
            .toList();
          var change = getChange(dailies);
          var std = getStd(dailies);
          var industryTop = topChange.getOrDefault(new IndustryService.IndustryYearlyKey(stock.getIndustry(), year), 0.0);
          return new Result(stock, change, std, industryTop);
        })
        .collect(Collectors.toSet());
    }
    return Set.of();
  }

  private double getChange(List<Daily> dailies) {
    return (dailies.getLast().getHfqClose() - dailies.getFirst().getHfqClose()) / dailies.getFirst().getHfqClose();
  }

  private double getStd(List<Daily> dailies) {
    var statistics = new DescriptiveStatistics();
    dailies.forEach(daily -> statistics.addValue(daily.getHfqClose()));
    return statistics.getStandardDeviation();
  }

  public record Result(Stock stock, double change, double std, double industryTop) {
    @Override
    public boolean equals(Object o) {
      if (o == null || getClass() != o.getClass()) return false;

      Result result = (Result) o;
      return stock.equals(result.stock);
    }

    @Override
    public int hashCode() {
      return stock.hashCode();
    }
  }
}
