package com.lanyuanxiaoyao.leopard.server.controller;

import cn.hutool.core.bean.BeanUtil;
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.service.StockService;
import com.lanyuanxiaoyao.leopard.server.entity.StockDetailVo;
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author lanyuanxiaoyao
 * @version 20250829
 */
@Slf4j
@RestController
@RequestMapping("stock")
public class StockController extends SimpleControllerSupport<Stock, Void, StockDetailVo, StockDetailVo> {
  private final StockService stockService;

  public StockController(StockService service, StockService stockService) {
    super(service);
    this.stockService = stockService;
  }

  @Override
  public GlobalResponse<Long> save(Void unused) {
    throw new UnsupportedOperationException();
  }

  @GetMapping("finance/{id}")
  public GlobalResponse<FinanceItem> finance(@PathVariable("id") Long id) {
    // 财报默认是上一年的
    var year = LocalDate.now().minusYears(1).getYear();
    var financeIndicator = stockService.findFinanceIndicator(id, year);
    return GlobalResponse.responseSuccess(new FinanceItem(
      id,
      year,
      financeIndicator
        .map(fi -> new BalanceSheetItem(
          NumberHelper.formatFinanceDouble(fi.getTotalAssets()),
          NumberHelper.formatFinanceDouble(fi.getCurrentAssets()),
          NumberHelper.formatPercentageDouble(fi.getCurrentAssetsToTotalAssetsRatio()),
          NumberHelper.formatFinanceDouble(fi.getFixedAssets()),
          NumberHelper.formatPercentageDouble(fi.getFixedAssetsToTotalAssetsRatio()),
          NumberHelper.formatFinanceDouble(fi.getTotalLiabilities()),
          NumberHelper.formatFinanceDouble(fi.getCurrentLiabilities()),
          NumberHelper.formatPercentageDouble(fi.getCurrentLiabilitiesToTotalAssetsRatio()),
          NumberHelper.formatFinanceDouble(fi.getLongTermLiabilities()),
          NumberHelper.formatPercentageDouble(fi.getLongTermLiabilitiesToTotalAssetsRatio())
        ))
        .orElse(new BalanceSheetItem()),
      financeIndicator
        .map(fi -> new IncomeItem(
          NumberHelper.formatFinanceDouble(fi.getOperatingRevenue()),
          NumberHelper.formatFinanceDouble(fi.getOperatingCost()),
          NumberHelper.formatFinanceDouble(fi.getOperatingProfit())
        ))
        .orElse(new IncomeItem()),
      financeIndicator
        .map(fi -> new CashFlowItem(
          NumberHelper.formatFinanceDouble(fi.getNetProfit()),
          NumberHelper.formatFinanceDouble(fi.getCashFlowFromOperatingActivities()),
          NumberHelper.formatFinanceDouble(fi.getCashFlowFromInvestingActivities()),
          NumberHelper.formatFinanceDouble(fi.getCashFlowFromFinancingActivities())
        ))
        .orElse(new CashFlowItem()),
      financeIndicator
        .map(fi -> new IndicateItem(
          NumberHelper.formatFinanceDouble(fi.getCurrentRatio()),
          NumberHelper.formatFinanceDouble(fi.getQuickRatio()),
          NumberHelper.formatFinanceDouble(fi.getReturnOnEquity()),
          NumberHelper.formatFinanceDouble(fi.getReturnOnAssets()),
          NumberHelper.formatFinanceDouble(fi.getAccountsReceivableTurnover()),
          NumberHelper.formatDaysDouble(fi.getDaysAccountsReceivableTurnover()),
          NumberHelper.formatFinanceDouble(fi.getInventoryTurnover()),
          NumberHelper.formatDaysDouble(fi.getDaysInventoryTurnover()),
          NumberHelper.formatFinanceDouble(fi.getFixedAssetsTurnover()),
          NumberHelper.formatDaysDouble(fi.getDaysFixedAssetsTurnover()),
          NumberHelper.formatFinanceDouble(fi.getTotalAssetsTurnover()),
          NumberHelper.formatDaysDouble(fi.getDaysTotalAssetsTurnover())
        ))
        .orElse(new IndicateItem())
    ));
  }

  @GetMapping("finance/{id}/{field}")
  public GlobalResponse<Map<String, Object>> financeCharts(@PathVariable("id") Long id, @PathVariable("field") String field) {
    var data = stockService.findFinanceIndicatorRecent(id, 5);
    var xList = new ArrayList<Integer>();
    var yList = new ArrayList<Object>();
    for (var indicator : data) {
      xList.add(indicator.getYear());
      yList.add(BeanUtil.getFieldValue(indicator, field));
    }
    return GlobalResponse.responseMapData(Map.of(
      "xList", xList, "yList", yList
    ));
  }

  @GetMapping("daily/current/{id}")
  public GlobalResponse<Map<String, Object>> dailyCurrent(@PathVariable("id") Long id) {
    var daily = stockService.findDailyLatest(id);
    return GlobalResponse.responseMapData(Map.of(
      "date", daily.map(Daily::getTradeDate).map(LocalDate::toString).orElse("/"),
      "open", daily.map(Daily::getOpen).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
      "close", daily.map(Daily::getClose).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
      "low", daily.map(Daily::getLow).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
      "high", daily.map(Daily::getHigh).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE)
    ));
  }

  @GetMapping("daily/{id}")
  public GlobalResponse<Map<String, Object>> dailyCharts(@PathVariable("id") Long id) {
    var data = stockService.findDailyRecent(id, 100);
    var xList = new ArrayList<String>();
    var yList = new ArrayList<List<Double>>();
    for (var daily : data) {
      xList.add(daily.getTradeDate().toString());
      yList.add(List.of(daily.getHfqOpen(), daily.getHfqClose(), daily.getHfqLow(), daily.getHfqHigh()));
    }
    return GlobalResponse.responseMapData(Map.of(
      "xList", xList, "yList", yList
    ));
  }

  @Override
  protected Function<Void, Stock> saveItemMapper() {
    throw new UnsupportedOperationException();
  }

  @Override
  protected Function<Stock, StockDetailVo> listItemMapper() {
    return StockDetailVo::of;
  }

  @Override
  protected Function<Stock, StockDetailVo> detailItemMapper() {
    return StockDetailVo::of;
  }

  public record FinanceItem(
    Long id,
    Integer year,
    BalanceSheetItem balanceSheet,
    IncomeItem income,
    CashFlowItem cashFlow,
    IndicateItem indicate
  ) {
  }

  public record BalanceSheetItem(
    String totalAssets,
    String currentAssets,
    String currentAssetsRatio,
    String fixedAssets,
    String fixedAssetsRatio,
    String totalLiabilities,
    String currentLiabilities,
    String currentLiabilitiesRatio,
    String longTermLiabilities,
    String longTermLiabilitiesRatio
  ) {
    public BalanceSheetItem() {
      this(
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE
      );
    }
  }

  public record IncomeItem(
    String operatingRevenue,
    String operatingCost,
    String operatingProfit
  ) {
    public IncomeItem() {
      this(
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE
      );
    }
  }

  public record CashFlowItem(
    String netProfit,
    String cashFlowFromOperatingActivities,
    String cashFlowFromInvestingActivities,
    String cashFlowFromFinancingActivities
  ) {
    public CashFlowItem() {
      this(
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE
      );
    }
  }

  public record IndicateItem(
    String currentRatio,
    String quickRatio,
    String roe,
    String roa,
    String accountsReceivableTurnover,
    String daysAccountsReceivableTurnover,
    String inventoryTurnover,
    String daysInventoryTurnover,
    String fixedAssetsTurnover,
    String daysFixedAssetsTurnover,
    String totalAssetsTurnover,
    String daysTotalAssetsTurnover
  ) {
    public IndicateItem() {
      this(
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE,
        NumberHelper.FINANCE_NULL_DOUBLE
      );
    }
  }
}
