package com.lanyuanxiaoyao.leopard.core.service.selector;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator;
import com.lanyuanxiaoyao.leopard.core.entity.QStock;
import com.lanyuanxiaoyao.leopard.core.helper.NumberHelper;
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.HashMap;
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;
import org.springframework.transaction.annotation.Transactional;

/**
 * 金字塔选股
 *
 * @author lanyuanxiaoyao
 * @version 20250924
 */
@Slf4j
@Service
public class PyramidStockSelector implements StockSelector<PyramidStockSelector.Request> {
  private final StockRepository stockRepository;

  public PyramidStockSelector(StockRepository stockRepository) {
    this.stockRepository = stockRepository;
  }

  @Transactional(readOnly = true)
  @Override
  public Set<Candidate> select(Request request) {
    // 选择至少有最近5年财报的股票
    // 有点奇怪，001400.SZ有近5年的财报但资料显示是2025年才上市的
    return stockRepository.findAll(QStock.stock.listedDate.before(LocalDate.of(request.year(), 1, 1)))
      .stream()
      .map(stock -> {
        var extra = new HashMap<String, String>();
        var score = 0;

        var recentIndicators = stock.getIndicators()
          .stream()
          .filter(indicator -> indicator.getYear() < request.year())
          .sorted((a, b) -> b.getYear() - a.getYear())
          .limit(5)
          .toList();
        if (recentIndicators.size() < 5) {
          return null;
        }
        var latestIndicator = recentIndicators.getFirst();

        var roeScore = 0;
        if (recentIndicators.stream().noneMatch(indicator -> indicator.getReturnOnEquity() == null || indicator.getReturnOnEquity() < 0)) {
          var averageRoe = recentIndicators.stream()
            .map(FinanceIndicator::getReturnOnEquity)
            .map(item -> ObjectUtil.defaultIfNull(item, 0.0))
            .mapToDouble(Double::doubleValue)
            .average()
            .orElse(0.0);
          if (averageRoe >= 35) {
            roeScore = 550;
          } else if (averageRoe >= 30) {
            roeScore = 500;
          } else if (averageRoe >= 25) {
            roeScore = 450;
          } else if (averageRoe >= 20) {
            roeScore = 400;
          } else if (averageRoe >= 15) {
            roeScore = 350;
          } else if (averageRoe >= 10) {
            roeScore = 300;
          }
          extra.put("平均ROE", NumberHelper.formatPriceDouble(averageRoe));
        }
        extra.put("平均ROE得分", NumberHelper.formatPriceDouble(roeScore));
        score += roeScore;

        var roaScore = 0;
        if (recentIndicators.stream().noneMatch(indicator -> indicator.getReturnOnAssets() == null)) {
          var averageRoa = recentIndicators.stream()
            .map(FinanceIndicator::getReturnOnAssets)
            .mapToDouble(Double::doubleValue)
            .average()
            .orElse(0.0);
          if (averageRoa >= 15) {
            roaScore = 100;
          } else if (averageRoa >= 11) {
            roaScore = 80;
          } else if (averageRoa >= 7) {
            roaScore = 50;
          }
          extra.put("平均ROA", NumberHelper.formatPriceDouble(averageRoa));
        }
        extra.put("平均ROA得分", NumberHelper.formatPriceDouble(roaScore));
        score += roaScore;

        var netProfitScore = 0;
        if (recentIndicators.stream().noneMatch(indicator -> indicator.getNetProfit() == null)) {
          var averageNetProfit = recentIndicators.stream()
            .map(FinanceIndicator::getNetProfit)
            .mapToDouble(Double::doubleValue)
            .average()
            .orElse(0.0);
          if (averageNetProfit >= 10000.0 * 10000000) {
            netProfitScore = 150;
          } else if (averageNetProfit >= 1000.0 * 10000000) {
            netProfitScore = 100;
          }
          extra.put("平均净利润", NumberHelper.formatPriceDouble(averageNetProfit));
        }
        extra.put("平均净利润得分", NumberHelper.formatPriceDouble(netProfitScore));
        score += netProfitScore;

        var cashScore = 0;
        if (
          ArrayUtil.isAllNotNull(latestIndicator.getTotalAssetsTurnover(), latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio())
            && (
            latestIndicator.getTotalAssetsTurnover() > 0.8 && latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio() >= 0.1
              || latestIndicator.getTotalAssetsTurnover() <= 0.8 && latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio() >= 0.2
          )
        ) {
          cashScore = 50;
        }
        extra.put("现金流得分", NumberHelper.formatPriceDouble(cashScore));
        score += cashScore;

        if (ObjectUtil.isNotNull(latestIndicator.getDaysAccountsReceivableTurnover()) && latestIndicator.getDaysAccountsReceivableTurnover() <= 30) {
          extra.put("应收账款周转天数得分", "20");
          score += 20;
        }
        if (ObjectUtil.isNotNull(latestIndicator.getDaysInventoryTurnover()) && latestIndicator.getDaysInventoryTurnover() <= 30) {
          extra.put("存货周转天数得分", "20");
          score += 20;
        }
        if (ArrayUtil.isAllNotNull(latestIndicator.getDaysAccountsReceivableTurnover(), latestIndicator.getDaysInventoryTurnover())) {
          if (latestIndicator.getDaysAccountsReceivableTurnover() + latestIndicator.getDaysInventoryTurnover() <= 40) {
            score += 20;
          } else if (latestIndicator.getDaysAccountsReceivableTurnover() + latestIndicator.getDaysInventoryTurnover() <= 60) {
            score += 10;
          }
        }

        if (recentIndicators.stream().noneMatch(indicator -> indicator.getOperatingGrossProfitMargin() == null)) {
          var stat = new DescriptiveStatistics();
          recentIndicators.stream()
            .map(FinanceIndicator::getOperatingGrossProfitMargin)
            .mapToDouble(Double::doubleValue)
            .forEach(stat::addValue);
          if (stat.getStandardDeviation() <= 0.3) {
            extra.put("毛利率标准差得分", "50");
            score += 50;
          }
        }

        var operatingSafeMarginScore = 0;
        if (ObjectUtil.isNotNull(latestIndicator.getOperatingSafetyMarginRatio())) {
          if (latestIndicator.getOperatingSafetyMarginRatio() >= 70) {
            operatingSafeMarginScore = 50;
          } else if (latestIndicator.getOperatingSafetyMarginRatio() >= 50) {
            operatingSafeMarginScore = 30;
          } else if (latestIndicator.getOperatingSafetyMarginRatio() >= 30) {
            operatingSafeMarginScore = 10;
          }
          extra.put("安全边际比率", NumberHelper.formatPriceDouble(latestIndicator.getOperatingSafetyMarginRatio()));
        }
        extra.put("安全边际比率得分", NumberHelper.formatPriceDouble(operatingSafeMarginScore));
        score += operatingSafeMarginScore;

        var netProfitAscendingScore = 0;
        if (recentIndicators.stream().noneMatch(indicator -> indicator.getNetProfit() == null)) {
          if (recentIndicators.get(0).getNetProfit() > recentIndicators.get(1).getNetProfit()) {
            netProfitAscendingScore += 30;
          } else {
            netProfitAscendingScore -= 30;
          }
          if (recentIndicators.get(1).getNetProfit() > recentIndicators.get(2).getNetProfit()) {
            netProfitAscendingScore += 25;
          } else {
            netProfitAscendingScore -= 25;
          }

          if (recentIndicators.get(2).getNetProfit() > recentIndicators.get(3).getNetProfit()) {
            netProfitAscendingScore += 20;
          } else {
            netProfitAscendingScore -= 20;
          }

          if (recentIndicators.get(3).getNetProfit() > recentIndicators.get(4).getNetProfit()) {
            netProfitAscendingScore += 15;
          } else {
            netProfitAscendingScore -= 15;
          }
        }
        extra.put("近五年净利润得分", NumberHelper.formatPriceDouble(netProfitAscendingScore));
        score += netProfitAscendingScore;

        var cashAscendingScore = 0;
        if (recentIndicators.stream().noneMatch(indicator -> indicator.getCashAndCashEquivalents() == null)) {
          if (recentIndicators.get(0).getCashAndCashEquivalents() > recentIndicators.get(1).getCashAndCashEquivalents()) {
            cashAscendingScore += 30;
          } else {
            cashAscendingScore -= 30;
          }

          if (recentIndicators.get(1).getCashAndCashEquivalents() > recentIndicators.get(2).getCashAndCashEquivalents()) {
            cashAscendingScore += 25;
          } else {
            cashAscendingScore -= 25;
          }

          if (recentIndicators.get(2).getCashAndCashEquivalents() > recentIndicators.get(3).getCashAndCashEquivalents()) {
            cashAscendingScore += 20;
          } else {
            cashAscendingScore -= 20;
          }

          if (recentIndicators.get(3).getCashAndCashEquivalents() > recentIndicators.get(4).getCashAndCashEquivalents()) {
            cashAscendingScore += 15;
          } else {
            cashAscendingScore -= 15;
          }
        }
        extra.put("近五年现金流得分", NumberHelper.formatPriceDouble(cashAscendingScore));
        score += cashAscendingScore;

        return new Candidate(stock, score, extra);
      })
      .filter(ObjectUtil::isNotNull)
      .sorted(Comparator.comparingDouble(Candidate::score).reversed())
      .limit(request.limit)
      .collect(Collectors.toSet());
  }

  public record Request(int year, int limit) {
    public Request(int year) {
      this(year, 50);
    }
  }
}
