import type { PublicModelPrice } from "../api/publicPricingClient";
import type { TextTokenCreditRate } from "./taskLifecycle";

const TOKENS_PER_MILLION = 1_000_000;
const BACKEND_TOKEN_PRICE_UNIT = 1_000;
const CREDITS_PER_CNY = 100;
const MILLS_PER_CNY = 1_000;
const CREDITS_PER_MILL = CREDITS_PER_CNY / MILLS_PER_CNY;

function isUsablePrice(value: number | null | undefined): value is number {
  return typeof value === "number" && Number.isFinite(value) && value >= 0;
}

function normalizeModelKey(value: string): string {
  return value.trim().toLowerCase();
}

function compactModelKey(value: string): string {
  return normalizeModelKey(value).replace(/[^a-z0-9]+/g, "");
}

function addCandidate(
  candidates: PublicModelPrice[],
  seen: Set<string>,
  price: PublicModelPrice,
): void {
  const key = normalizeModelKey(price.modelKey);
  if (seen.has(key)) return;
  seen.add(key);
  candidates.push(price);
}

export function millsPerThousandTokensToCreditsPerMillion(
  priceMills: number,
): number {
  if (!isUsablePrice(priceMills)) return 0;
  return (
    priceMills *
    (TOKENS_PER_MILLION / BACKEND_TOKEN_PRICE_UNIT) *
    CREDITS_PER_MILL
  );
}

export function modelPriceToTextTokenCreditRate(
  price: PublicModelPrice,
): TextTokenCreditRate | null {
  if (
    !isUsablePrice(price.inputPriceMills) ||
    !isUsablePrice(price.outputPriceMills)
  )
    return null;

  return {
    inputCreditsPerMillion: millsPerThousandTokensToCreditsPerMillion(
      price.inputPriceMills,
    ),
    outputCreditsPerMillion: millsPerThousandTokensToCreditsPerMillion(
      price.outputPriceMills,
    ),
    source: "server",
    modelKey: price.modelKey,
  };
}

export function resolveTextTokenCreditRate(
  prices: PublicModelPrice[],
  modelKey: string | null | undefined,
): TextTokenCreditRate | null {
  const normalizedTarget = normalizeModelKey(modelKey || "");
  if (!normalizedTarget) return null;

  const compactTarget = compactModelKey(normalizedTarget);
  const candidates: PublicModelPrice[] = [];
  const seen = new Set<string>();

  for (const price of prices) {
    if (normalizeModelKey(price.modelKey) === normalizedTarget) {
      addCandidate(candidates, seen, price);
    }
  }

  for (const price of prices) {
    if (compactModelKey(price.modelKey) === compactTarget) {
      addCandidate(candidates, seen, price);
    }
  }

  for (const price of prices) {
    const compactPriceKey = compactModelKey(price.modelKey);
    if (
      compactPriceKey.includes(compactTarget) ||
      compactTarget.includes(compactPriceKey)
    ) {
      addCandidate(candidates, seen, price);
    }
  }

  for (const price of candidates) {
    const rate = modelPriceToTextTokenCreditRate(price);
    if (rate) return rate;
  }

  return null;
}
