const EVM_NATIVE_DECIMALS = 18;
const SOLANA_NATIVE_DECIMALS = 9;

export function formatBaseUnitAmount(rawAmount: string | number | bigint, decimals: number): string {
  const normalizedDecimals = Number.isFinite(decimals) && decimals >= 0 ? Math.trunc(decimals) : 0;
  const raw = BigInt(String(rawAmount));
  const negative = raw < 0n;
  const absolute = negative ? -raw : raw;

  if (normalizedDecimals === 0) {
    return `${negative ? "-" : ""}${absolute.toString(10)}`;
  }

  const divisor = 10n ** BigInt(normalizedDecimals);
  const whole = absolute / divisor;
  const fraction = absolute % divisor;

  if (fraction === 0n) {
    return `${negative ? "-" : ""}${whole.toString(10)}`;
  }

  const fractionDigits = fraction.toString(10).padStart(normalizedDecimals, "0").replace(/0+$/, "");

  return `${negative ? "-" : ""}${whole.toString(10)}.${fractionDigits}`;
}

export function normalizeEvmAmount(input: {
  rawValue?: string | null;
  decimals?: number | string | null;
  value?: string | number | null;
  isNative: boolean;
}): string | null {
  if (input.rawValue && input.rawValue !== "0x") {
    const raw = hexToDecimalString(input.rawValue);
    const decimals = resolveDecimals(input.decimals, input.isNative ? EVM_NATIVE_DECIMALS : null);

    if (decimals !== null) {
      return formatBaseUnitAmount(raw, decimals);
    }

    return raw;
  }

  if (input.value === null || input.value === undefined || input.value === "") {
    return null;
  }

  return String(input.value);
}

export function normalizeSolanaNativeAmount(rawAmount: string | number | bigint): string {
  return formatBaseUnitAmount(rawAmount, SOLANA_NATIVE_DECIMALS);
}

export function normalizeSolanaTokenAmount(
  rawAmount: string | number | bigint,
  decimals?: number | null,
  fallbackValue?: string | number | null
): string {
  const resolvedDecimals = resolveDecimals(decimals, null);

  if (resolvedDecimals !== null) {
    return formatBaseUnitAmount(rawAmount, resolvedDecimals);
  }

  if (fallbackValue !== null && fallbackValue !== undefined && fallbackValue !== "") {
    return String(fallbackValue);
  }

  return String(rawAmount);
}

function resolveDecimals(value: number | string | null | undefined, fallback: number | null): number | null {
  if (typeof value === "number" && Number.isFinite(value)) {
    return Math.trunc(value);
  }

  if (typeof value === "string" && value.trim()) {
    const parsed = Number.parseInt(value, 10);
    if (!Number.isNaN(parsed)) {
      return parsed;
    }
  }

  return fallback;
}

function hexToDecimalString(value: string): string {
  if (!value || value === "0x") {
    return "0";
  }

  return BigInt(value).toString(10);
}
