export interface VolatilityEstimatorOptions {
  windowMinutes?: number;
  minSamples?: number;
  maxCadenceMs?: number;
}

interface PricePoint {
  ts: number;
  price: number;
}

/**
 * Estimates short-term volatility based on rolling mid-price snapshots.
 */
export class VolatilityEstimator {
  private readonly windowMinutes: number;
  private readonly minSamples: number;
  private readonly maxCadenceMs: number;
  private readonly history: PricePoint[] = [];

  constructor(options: VolatilityEstimatorOptions = {}) {
    this.windowMinutes = options.windowMinutes ?? 30;
    this.minSamples = options.minSamples ?? 10;
    this.maxCadenceMs = options.maxCadenceMs ?? 5_000;
  }

  update(price: number, ts: number = Date.now()): void {
    if (!Number.isFinite(price) || price <= 0) return;
    const last = this.history[this.history.length - 1];
    if (last && ts - last.ts < this.maxCadenceMs) {
      this.history[this.history.length - 1] = { ts, price };
    } else {
      this.history.push({ ts, price });
    }

    const cutoff = ts - this.windowMinutes * 60 * 1000;
    while (this.history.length > 0 && this.history[0]!.ts < cutoff) {
      this.history.shift();
    }
  }

  getAnnualizedVolatility(): number | undefined {
    if (this.history.length < this.minSamples) return undefined;
    const returns = this.computeLogReturns(this.history);
    if (returns.length === 0) return undefined;
    const stdDev = this.standardDeviation(returns);
    const periodsPerYear = (365 * 24 * 60) / this.windowMinutes;
    return stdDev * Math.sqrt(periodsPerYear);
  }

  getHourlyVolatilityBps(): number | undefined {
    if (this.history.length < 2) return undefined;
    const windowMs = 60 * 60 * 1000;
    const latest = this.history[this.history.length - 1]!;
    const cutoff = latest.ts - windowMs;
    const relevant = this.history.filter(point => point.ts >= cutoff);
    if (relevant.length < 2) return undefined;
    const first = relevant[0]!.price;
    const last = relevant[relevant.length - 1]!.price;
    const range = Math.abs(last - first) / first;
    return range * 10_000;
  }

  getStatus() {
    const oldest = this.history[0];
    const latest = this.history[this.history.length - 1];
    return {
      historySize: this.history.length,
      oldestTs: oldest?.ts,
      latestPrice: latest?.price,
      annualizedVol: this.getAnnualizedVolatility(),
      hourlyVolBps: this.getHourlyVolatilityBps()
    };
  }

  private computeLogReturns(series: PricePoint[]): number[] {
    const returns: number[] = [];
    for (let i = 1; i < series.length; i += 1) {
      const prev = series[i - 1]!.price;
      const curr = series[i]!.price;
      if (prev <= 0 || curr <= 0) continue;
      returns.push(Math.log(curr / prev));
    }
    return returns;
  }

  private standardDeviation(values: number[]): number {
    if (values.length === 0) return 0;
    const mean = values.reduce((sum, v) => sum + v, 0) / values.length;
    const variance =
      values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;
    return Math.sqrt(variance);
  }
}

