import type {DashboardData} from './dashboard';
import {TOKEN_SCALE, rialToToman, tokenDecimal, type TradeSide} from './trade.ts';
import {wallexAssets, wallexFresh, type WallexQuote, type WallexSymbol} from './wallex.ts';

const ZERO = BigInt(0);
const ONE = BigInt(1);
const TEN = BigInt(10);
const MAX_RIAL = BigInt(Number.MAX_SAFE_INTEGER);

function decimal(value: unknown, maxScale: number, localized = false): {units: bigint; scale: number} | null {
  if (typeof value !== 'string' || value.length > 100) return null;
  const text = localized
    ? value.replace(/[۰-۹]/g, d => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)))
      .replace(/[٠-٩]/g, d => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)))
      .replace(/[,٬\s]/g, '').replace(/٫/g, '.')
    : value;
  if (!/^\d+(?:\.\d+)?$/.test(text)) return null;
  const [whole, fraction = ''] = text.split('.');
  if (fraction.length > maxScale) return null;
  return {units: BigInt(whole + fraction), scale: fraction.length};
}

function quoteTerms(quote: WallexQuote | undefined, now: number) {
  if (!quote || !Number.isFinite(now) || !wallexFresh(quote, now)) return null;
  const asset = wallexAssets.find(asset => asset.symbol === quote.symbol);
  if (!asset || asset.base !== quote.base || !Number.isInteger(quote.amountPrecision)
    || quote.amountPrecision === null || quote.amountPrecision < 0 || quote.amountPrecision > 12) return null;
  const price = decimal(quote.price, 79);
  if (!price || quote.price.length > 80 || price.units <= ZERO) return null;
  return {precision: quote.amountPrecision, priceNumerator: price.units * TEN, priceDenominator: TEN ** BigInt(price.scale)};
}

export type MarketTradeCalculation = {
  quantity: string;
  grossToman: string;
  unusedToman: string;
  tokenAtomic: bigint;
  grossRial: bigint;
  error: 'amount' | 'minimum' | 'range' | 'quote' | null;
};

// Estimates only. Cash settles in whole rial; quantities use the instrument's precision.
export function calculateMarketTrade(input: string, side: TradeSide, quote: WallexQuote | undefined, now = Date.now()): MarketTradeCalculation {
  const empty = (error: MarketTradeCalculation['error']): MarketTradeCalculation => ({quantity: '0', grossToman: '0', unusedToman: '0', tokenAtomic: ZERO, grossRial: ZERO, error});
  const terms = quoteTerms(quote, now);
  if (!terms) return empty('quote');
  if (side !== 'buy' && side !== 'sell') return empty('amount');
  const amount = decimal(input, side === 'buy' ? 1 : terms.precision, true);
  if (!amount || amount.units <= ZERO) return empty('amount');

  const quantityScale = TEN ** BigInt(terms.precision);
  const budgetRial = side === 'buy' ? amount.units * TEN ** BigInt(1 - amount.scale) : ZERO;
  if (budgetRial > MAX_RIAL) return empty('range');
  const quantityUnits = side === 'buy'
    ? budgetRial * terms.priceDenominator * quantityScale / terms.priceNumerator
    : amount.units * TEN ** BigInt(terms.precision - amount.scale);
  if (quantityUnits === ZERO) return empty('minimum');
  const tokenAtomic = quantityUnits * (TOKEN_SCALE / quantityScale);
  if (tokenAtomic > MAX_RIAL * TOKEN_SCALE) return empty('range');

  const numerator = quantityUnits * terms.priceNumerator;
  const denominator = quantityScale * terms.priceDenominator;
  // Buy cost rounds up to avoid an understated charge; sell proceeds round down.
  const grossRial = side === 'buy' ? (numerator + denominator - ONE) / denominator : numerator / denominator;
  if (grossRial === ZERO) return empty('minimum');
  if (grossRial > MAX_RIAL) return empty('range');
  return {quantity: tokenDecimal(tokenAtomic), grossToman: rialToToman(grossRial), unusedToman: side === 'buy' ? rialToToman(budgetRial - grossRial) : '0', tokenAtomic, grossRial, error: null};
}

export function availableMarketTradeBalances(positions: DashboardData['positions'], symbol: WallexSymbol): {cashRial: bigint | null; tokenAtomic: bigint | null} {
  const asset = wallexAssets.find(asset => asset.symbol === symbol);
  let cashAtomic = ZERO, tokenAtomic = ZERO, cashValid = true, tokenValid = !!asset;
  for (const row of positions) {
    const cash = row.project_id === null && (row.asset_symbol === 'TMN' || row.asset_symbol === 'IRR');
    const token = row.project_id === null && row.asset_symbol === asset?.base;
    if (!cash && !token) continue;
    const total = decimal(row.quantity, 18), locked = decimal(row.locked_quantity, 18);
    const totalAtomic = total && total.units * TEN ** BigInt(18 - total.scale);
    const lockedAtomic = locked && locked.units * TEN ** BigInt(18 - locked.scale);
    if (totalAtomic === null || lockedAtomic === null || lockedAtomic > totalAtomic) {
      if (cash) cashValid = false;
      if (token) tokenValid = false;
      continue;
    }
    const available = totalAtomic - lockedAtomic;
    if (cash) cashAtomic += available * (row.asset_symbol === 'TMN' ? TEN : ONE);
    if (token) tokenAtomic += available;
  }
  return {cashRial: cashValid ? cashAtomic / TOKEN_SCALE : null, tokenAtomic: tokenValid ? tokenAtomic : null};
}

export function marketTradeAmountAtPercent(side: TradeSide, percent: number, cashRial: bigint | null, tokenAtomic: bigint | null, quote: WallexQuote | undefined): string {
  const terms = quoteTerms(quote, Date.now());
  if (!terms || !Number.isInteger(percent) || percent < 1 || percent > 100) return '';
  const percentage = BigInt(percent), hundred = BigInt(100);
  if (side === 'buy') {
    if (typeof cashRial !== 'bigint' || cashRial < ZERO || cashRial > MAX_RIAL) return '';
    return rialToToman(cashRial * percentage / hundred);
  }
  if (side !== 'sell' || typeof tokenAtomic !== 'bigint' || tokenAtomic < ZERO || tokenAtomic > MAX_RIAL * TOKEN_SCALE) return '';
  const quantum = TOKEN_SCALE / TEN ** BigInt(terms.precision);
  return tokenDecimal((tokenAtomic * percentage / hundred / quantum) * quantum);
}
