import type { TradeType } from "../exchange/types.js";
import * as D from "../util/decimal.js";

/**
 * Motor de auto-pricing: função PURA (sem I/O), fácil de testar. Aplica as
 * três travas do CLAUDE.md na ordem:
 *   1. seguir concorrente (beat_top | match_rank)
 *   2. piso de lucro (min_profit_price)
 *   3. piso de spot (spot * spot_margin_ratio)
 *
 * Preço final:
 *   SELL -> max(alvo, piso_lucro, piso_spot)
 *   BUY  -> min(alvo, piso_lucro, piso_spot)
 *
 * "Melhor" preço depende do lado: vendendo, melhor = mais barato; comprando,
 * melhor = mais caro. Por isso o concorrente "topo" é o de menor preço (SELL)
 * ou maior preço (BUY).
 */

export interface PricingCompetitor {
  advNo: string;
  advertiserId: string | null;
  price: string;
}

export interface PricingConfig {
  followStrategy: "beat_top" | "match_rank";
  beatAmount: string | null;
  targetRank: number | null;
  minProfitPrice: string | null;
  spotGuardEnabled: boolean;
  spotMarginRatio: string | null;
  minPriceStep: string | null;
}

export interface PricingInput {
  tradeType: TradeType;
  currentPrice: string | null;
  competitors: PricingCompetitor[];
  config: PricingConfig;
  spotPrice: string | null;
}

export type PricingReason =
  | "seguir_concorrente"
  | "piso_lucro"
  | "piso_spot"
  | "sem_concorrentes"
  | "sem_alvo";

export interface PricingDecision {
  finalPrice: string | null;
  competitorPrice: string | null;
  spotFloor: string | null;
  reason: PricingReason;
  warnings: string[];
  shouldUpdate: boolean;
}

export function computeTargetPrice(input: PricingInput): PricingDecision {
  const { tradeType, config } = input;
  const isSell = tradeType === "SELL";
  const warnings: string[] = [];

  // 1) Ordena concorrentes por competitividade (rank 1 = mais competitivo).
  const sorted = [...input.competitors].sort((a, b) => {
    const c = D.cmp(D.parse(a.price), D.parse(b.price));
    return isSell ? c : -c; // SELL: asc (menor = topo); BUY: desc (maior = topo)
  });

  if (sorted.length === 0) {
    return {
      finalPrice: null,
      competitorPrice: null,
      spotFloor: null,
      reason: "sem_concorrentes",
      warnings,
      shouldUpdate: false,
    };
  }

  // Preço de referência do concorrente conforme a estratégia.
  let refIdx = 0;
  if (config.followStrategy === "match_rank") {
    const rank = Math.max(1, config.targetRank ?? 1);
    refIdx = Math.min(rank - 1, sorted.length - 1);
  }
  const competitorPrice = sorted[refIdx].price;
  const competitorD = D.parse(competitorPrice);

  // Alvo: beat_top desloca por beat_amount; match_rank casa exatamente.
  let targetD = competitorD;
  if (config.followStrategy === "beat_top" && config.beatAmount) {
    const beat = D.parse(config.beatAmount);
    targetD = isSell ? D.sub(competitorD, beat) : D.add(competitorD, beat);
  }

  // 2) e 3) travas
  const candidates: Array<{ value: bigint; reason: PricingReason }> = [
    { value: targetD, reason: "seguir_concorrente" },
  ];

  if (config.minProfitPrice) {
    candidates.push({ value: D.parse(config.minProfitPrice), reason: "piso_lucro" });
  }

  let spotFloor: string | null = null;
  if (config.spotGuardEnabled) {
    if (input.spotPrice && config.spotMarginRatio) {
      const floorD = D.mul(D.parse(input.spotPrice), D.parse(config.spotMarginRatio));
      spotFloor = D.format(floorD);
      candidates.push({ value: floorD, reason: "piso_spot" });
    } else {
      // Fallback (CLAUDE.md): sem spot 1:1, desativa a trava e avisa.
      warnings.push(
        "Trava de spot ativada mas sem preço spot/margem disponível — trava ignorada para este anúncio.",
      );
    }
  }

  // SELL -> o maior dos candidatos protege; BUY -> o menor.
  let chosen = candidates[0];
  for (const c of candidates) {
    const better = isSell ? c.value > chosen.value : c.value < chosen.value;
    if (better) chosen = c;
  }
  const finalD = chosen.value;
  const finalPrice = D.format(finalD);

  // shouldUpdate: respeita min_price_step para poupar rate-limit.
  let shouldUpdate: boolean;
  if (input.currentPrice === null) {
    shouldUpdate = true;
  } else {
    const diff = D.abs(D.sub(finalD, D.parse(input.currentPrice)));
    const step = config.minPriceStep ? D.parse(config.minPriceStep) : 0n;
    shouldUpdate = D.cmp(diff, step) > 0;
  }

  return {
    finalPrice,
    competitorPrice,
    spotFloor,
    reason: chosen.reason,
    warnings,
    shouldUpdate,
  };
}
