"use client";

import { createContext, useContext, useState, useCallback, useRef, useEffect, useSyncExternalStore, type ReactNode } from "react";
import dayjs from "dayjs";
import { type TimeRange } from "@/lib/utils";

interface TimeRangeContextType {
  range: TimeRange;
  setRange: (r: TimeRange) => void;
  customStart: string;
  customEnd: string;
  setCustomStart: (s: string) => void;
  setCustomEnd: (s: string) => void;
  /** Returns { start?, end? } unix timestamps (seconds) ready for API calls */
  getEffectiveRange: () => { start?: number; end?: number };
}

const TimeRangeContext = createContext<TimeRangeContextType>({
  range: "7d",
  setRange: () => {},
  customStart: "",
  customEnd: "",
  setCustomStart: () => {},
  setCustomEnd: () => {},
  getEffectiveRange: () => ({}),
});

const STORAGE_KEY = "time-range";
const DEFAULT_RANGE: TimeRange = "7d";

let listeners: Array<() => void> = [];
function subscribe(cb: () => void) {
  listeners = [...listeners, cb];
  return () => { listeners = listeners.filter(l => l !== cb); };
}
function emitChange() {
  for (const l of listeners) l();
}

function getSnapshot(): string {
  return localStorage.getItem(STORAGE_KEY) ?? "";
}
function getServerSnapshot(): string {
  return "";
}

function persist(range: TimeRange, customStart: string, customEnd: string) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify({ range, customStart, customEnd }));
  emitChange();
}

/** Convert a preset to concrete date strings */
function presetDates(preset: TimeRange): { start: string; end: string } {
  const today = dayjs().format("YYYY-MM-DD");
  switch (preset) {
    case "today":
      return { start: today, end: today };
    case "7d":
      return { start: dayjs().subtract(6, "day").format("YYYY-MM-DD"), end: today };
    case "30d":
      return { start: dayjs().subtract(29, "day").format("YYYY-MM-DD"), end: today };
    default:
      return { start: today, end: today };
  }
}

function initDates(saved: { range?: TimeRange; customStart?: string; customEnd?: string } | null) {
  if (saved?.customStart && saved?.customEnd) {
    return { start: saved.customStart, end: saved.customEnd };
  }
  const r = (saved?.range as TimeRange) ?? DEFAULT_RANGE;
  if (r === "all") return { start: "", end: "" };
  return presetDates(r);
}

export function TimeRangeProvider({ children }: { children: ReactNode }) {
  const raw = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);

  const saved = (() => {
    if (!raw) return null;
    try { return JSON.parse(raw); } catch { return null; }
  })();

  const init = initDates(saved);
  const [range, setRangeState] = useState<TimeRange>(saved?.range ?? DEFAULT_RANGE);
  const [customStart, setCustomStartState] = useState(init.start);
  const [customEnd, setCustomEndState] = useState(init.end);

  const rangeRef = useRef(range);
  const customStartRef = useRef(customStart);
  const customEndRef = useRef(customEnd);
  useEffect(() => { rangeRef.current = range; }, [range]);
  useEffect(() => { customStartRef.current = customStart; }, [customStart]);
  useEffect(() => { customEndRef.current = customEnd; }, [customEnd]);

  // Fetch actual date boundaries when "all" is selected (including on mount)
  const fetchDateRange = useCallback(async () => {
    try {
      const res = await fetch("/api/date-range");
      const { minDate, maxDate } = await res.json();
      if (minDate && maxDate) {
        setCustomStartState(minDate);
        setCustomEndState(maxDate);
        persist("all", minDate, maxDate);
      }
    } catch { /* ignore */ }
  }, []);

  // On mount: if saved range is "all", fetch real boundaries
  useEffect(() => {
    if (range === "all") fetchDateRange();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const setRange = useCallback((r: TimeRange) => {
    setRangeState(r);
    if (r === "all") {
      // Query first, then backfill dates
      fetchDateRange();
    } else if (r !== "custom") {
      const { start, end } = presetDates(r);
      setCustomStartState(start);
      setCustomEndState(end);
      persist(r, start, end);
    } else {
      persist(r, customStartRef.current, customEndRef.current);
    }
  }, [fetchDateRange]);

  const setCustomStart = useCallback((s: string) => {
    setCustomStartState(s);
    persist(rangeRef.current, s, customEndRef.current);
  }, []);

  const setCustomEnd = useCallback((e: string) => {
    setCustomEndState(e);
    persist(rangeRef.current, customStartRef.current, e);
  }, []);

  // Always derive timestamps from customStart/customEnd — single source of truth
  const getEffectiveRange = useCallback(() => {
    // "all" with dates not yet loaded → no filter
    if (range === "all" && !customStart && !customEnd) return {};
    const result: { start?: number; end?: number } = {};
    if (customStart) result.start = dayjs(customStart).startOf("day").unix();
    if (customEnd) result.end = dayjs(customEnd).endOf("day").unix();
    return result;
  }, [range, customStart, customEnd]);

  return (
    <TimeRangeContext.Provider value={{
      range, setRange,
      customStart, customEnd,
      setCustomStart,
      setCustomEnd,
      getEffectiveRange,
    }}>
      {children}
    </TimeRangeContext.Provider>
  );
}

export function useTimeRange() {
  return useContext(TimeRangeContext);
}
