import type { PricePoint } from "@/lib/playground/stockTradingEnv";

type AlphaVantageDailyResponse = {
  "Meta Data"?: Record<string, string>;
  "Time Series (Daily)"?: Record<
    string,
    {
      "1. open"?: string;
      "2. high"?: string;
      "3. low"?: string;
      "4. close"?: string;
      "5. volume"?: string;
    }
  >;
  "Error Message"?: string;
  Note?: string;
};

export function alphaVantageDailyToPoints(json: unknown): { points: PricePoint[]; metaSymbol?: string; error?: string } {
  const data = json as AlphaVantageDailyResponse;
  if (data?.["Error Message"]) return { points: [], error: data["Error Message"] };
  if (data?.Note) return { points: [], error: data.Note };

  const meta = data?.["Meta Data"];
  const metaSymbol =
    meta && typeof meta === "object"
      ? (Object.entries(meta).find(([k]) => k.toLowerCase().includes("symbol"))?.[1] ?? undefined)
      : undefined;

  const ts = data?.["Time Series (Daily)"];
  if (!ts) return { points: [], error: "Alpha Vantage: missing Time Series (Daily)" };

  const points: PricePoint[] = [];
  for (const [dateStr, row] of Object.entries(ts)) {
    const open = row?.["1. open"] != null ? Number(row["1. open"]) : NaN;
    const high = row?.["2. high"] != null ? Number(row["2. high"]) : NaN;
    const low = row?.["3. low"] != null ? Number(row["3. low"]) : NaN;
    const closeStr = row?.["4. close"];
    const close = closeStr ? Number(closeStr) : NaN;
    const volume = row?.["5. volume"] != null ? Number(row["5. volume"]) : NaN;
    const t = Math.floor(Date.parse(`${dateStr}T00:00:00Z`) / 1000);
    if (!Number.isFinite(t) || !Number.isFinite(close) || close <= 0) continue;
    points.push({
      t,
      open: Number.isFinite(open) ? open : undefined,
      high: Number.isFinite(high) ? high : undefined,
      low: Number.isFinite(low) ? low : undefined,
      close,
      volume: Number.isFinite(volume) ? volume : undefined,
    });
  }

  points.sort((a, b) => a.t - b.t);
  return { points, metaSymbol };
}

export function resampleWeekly(points: PricePoint[]): PricePoint[] {
  // Aggregate into weekly OHLCV (approx, using UTC and week-of-year buckets).
  const byWeek = new Map<
    string,
    { firstT: number; lastT: number; open?: number; high?: number; low?: number; close: number; volume?: number }
  >();

  for (const p of points) {
    const d = new Date(p.t * 1000);
    const year = d.getUTCFullYear();
    const jan1 = new Date(Date.UTC(year, 0, 1));
    const dayOfYear = Math.floor((d.getTime() - jan1.getTime()) / 86400000) + 1;
    const week = Math.ceil(dayOfYear / 7);
    const key = `${year}-W${week}`;

    const prev = byWeek.get(key);
    if (!prev) {
      byWeek.set(key, {
        firstT: p.t,
        lastT: p.t,
        open: p.open,
        high: p.high,
        low: p.low,
        close: p.close,
        volume: p.volume,
      });
      continue;
    }

    // Update open (earliest).
    if (p.t < prev.firstT) {
      prev.firstT = p.t;
      prev.open = p.open ?? prev.open;
    }
    // Update close (latest).
    if (p.t >= prev.lastT) {
      prev.lastT = p.t;
      prev.close = p.close;
    }
    // Update high/low.
    if (p.high != null && Number.isFinite(p.high)) prev.high = prev.high == null ? p.high : Math.max(prev.high, p.high);
    if (p.low != null && Number.isFinite(p.low)) prev.low = prev.low == null ? p.low : Math.min(prev.low, p.low);
    // Sum volume.
    if (p.volume != null && Number.isFinite(p.volume))
      prev.volume = (prev.volume ?? 0) + p.volume;
  }

  const out: PricePoint[] = [];
  for (const w of byWeek.values()) {
    out.push({
      t: w.lastT,
      open: w.open,
      high: w.high,
      low: w.low,
      close: w.close,
      volume: w.volume,
    });
  }
  out.sort((a, b) => a.t - b.t);
  return out;
}

export function clipToRange(points: PricePoint[], range: "6mo" | "1y" | "2y" | "5y"): PricePoint[] {
  const tradingDays =
    range === "6mo" ? 126 : range === "1y" ? 252 : range === "2y" ? 504 : 1260;
  if (points.length <= tradingDays) return points;
  return points.slice(points.length - tradingDays);
}
