/**
 * CS-style float / wear for inventory instances.
 *
 * Item.marketValue = Field-Tested reference price (mid-band ≈ 1.00×).
 * Per-drop float f ∈ [0, 1) drives wear grade and price multiplier.
 */

import { toCentsBigInt } from './cents.js';

export const WEAR_BANDS = [
  {
    wear: 'Factory New',
    min: 0,
    max: 0.07,
    multBest: 1.5,
    multWorst: 1.35,
  },
  {
    wear: 'Minimal Wear',
    min: 0.07,
    max: 0.15,
    multBest: 1.3,
    multWorst: 1.15,
  },
  {
    wear: 'Field-Tested',
    min: 0.15,
    max: 0.38,
    multBest: 1.05,
    multWorst: 0.8,
  },
  {
    wear: 'Well-Worn',
    min: 0.38,
    max: 0.45,
    multBest: 0.8,
    multWorst: 0.6,
  },
  {
    wear: 'Battle-Scarred',
    min: 0.45,
    max: 1,
    multBest: 0.55,
    multWorst: 0.4,
  },
];

/** Mid Field-Tested float → ~1.00× catalog price */
export const FT_MID_FLOAT = 0.265;

export function clampFloat(f) {
  const n = Number(f);
  if (!Number.isFinite(n)) return 0;
  if (n < 0) return 0;
  if (n >= 1) return 0.999999;
  return n;
}

export function wearFromFloat(floatValue) {
  const f = clampFloat(floatValue);
  for (const band of WEAR_BANDS) {
    if (f >= band.min && f < band.max) return band.wear;
  }
  return 'Battle-Scarred';
}

function bandForFloat(floatValue) {
  const f = clampFloat(floatValue);
  for (const band of WEAR_BANDS) {
    if (f >= band.min && f < band.max) return band;
  }
  return WEAR_BANDS[WEAR_BANDS.length - 1];
}

function lerp(a, b, t) {
  return a + (b - a) * t;
}

/** Position in wear band: 0 = best (low float), 1 = worst (high float) */
export function priceMultiplier(floatValue) {
  const f = clampFloat(floatValue);
  const band = bandForFloat(f);
  const span = band.max - band.min;

  // Field-Tested: pin mid-band to 1.00× catalog (better half → 1.05, worse → 0.80)
  if (band.wear === 'Field-Tested') {
    const mid = (band.min + band.max) / 2;
    if (f <= mid) {
      const t = mid > band.min ? (f - band.min) / (mid - band.min) : 0;
      return lerp(band.multBest, 1, Math.min(1, Math.max(0, t)));
    }
    const t = band.max > mid ? (f - mid) / (band.max - mid) : 0;
    return lerp(1, band.multWorst, Math.min(1, Math.max(0, t)));
  }

  const t = span > 0 ? (f - band.min) / span : 0;
  return lerp(band.multBest, band.multWorst, Math.min(1, Math.max(0, t)));
}

export function computeValueCents(baseMarketValue, floatValue) {
  // Always BigInt-parse — Number() silently corrupts values above ~9e15
  let base = toCentsBigInt(baseMarketValue);
  if (base < 0n) base = 0n;
  if (base <= 0n) return 0n;
  // Fixed-point multiplier (×10000) to avoid Number precision loss on huge bases
  const multBp = Math.round(priceMultiplier(floatValue) * 10000);
  const cents = (base * BigInt(Math.max(0, multBp)) + 5000n) / 10000n;
  return cents < 1n ? 1n : cents;
}

export function rollFloat() {
  return Math.random();
}

export function rollPaintSeed() {
  return Math.floor(Math.random() * 1000);
}

/**
 * Quality skill: pull floats below Field-Tested (WW/BS, float ≥ 0.38)
 * toward FT band. Clamp so result stays ≥ FT min (0.15).
 */
export function applyQualityUpgrade(floatValue, qualityBonus = 0) {
  let f = clampFloat(floatValue);
  const levels = Math.max(0, Math.floor(Number(qualityBonus) || 0));
  if (levels <= 0 || f < 0.38) return f;
  f = clampFloat(f - levels * 0.02);
  return Math.max(0.15, f);
}

/** Full roll for a new inventory instance from catalog marketValue */
export function rollInstanceValue(baseMarketValue, wearBonus = 0, qualityBonus = 0) {
  let floatValue = rollFloat();
  const bias = Math.min(0.55, Math.max(0, Number(wearBonus) || 0) * 0.008);
  if (bias > 0) {
    floatValue = clampFloat(floatValue * (1 - bias));
  }
  floatValue = applyQualityUpgrade(floatValue, qualityBonus);
  return {
    floatValue,
    wear: wearFromFloat(floatValue),
    paintSeed: rollPaintSeed(),
    valueCents: computeValueCents(baseMarketValue, floatValue),
  };
}
