import { useReducer, useEffect, useRef, useCallback, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import type { HighlightsData } from "../shared/types";
import { getHighlights } from "../services/reportService";
import { defaultReferencePeriod } from "../utils/referencePeriod";

interface State {
  data: HighlightsData | null;
  windowDays: 30 | 60 | 90;
  isLoading: boolean;
  error: string | null;
}

type Action =
  | { type: "SET_LOADING"; payload: boolean }
  | { type: "SET_DATA"; payload: HighlightsData }
  | { type: "SET_ERROR"; payload: string }
  | { type: "SET_WINDOW_DAYS"; payload: 30 | 60 | 90 };

const initialState: State = {
  data: null,
  windowDays: 30,
  isLoading: false,
  error: null,
};

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "SET_LOADING":
      return { ...state, isLoading: action.payload };
    case "SET_DATA":
      return { ...state, data: action.payload, isLoading: false, error: null };
    case "SET_ERROR":
      return { ...state, error: action.payload, isLoading: false };
    case "SET_WINDOW_DAYS":
      return { ...state, windowDays: action.payload };
    default:
      return state;
  }
}

/**
 * Parses `?refY=YYYY&refM=MM` from the search string. Falls back to the
 * previous-month default when either is missing or invalid. Exposed for
 * unit tests.
 */
export function resolveHighlightsReference(
  rawYear: string | null,
  rawMonth: string | null,
  today: Date = new Date(),
): { year: number; month: number } {
  const y = rawYear !== null ? Number(rawYear) : NaN;
  const m = rawMonth !== null ? Number(rawMonth) : NaN;
  if (
    Number.isInteger(y) &&
    Number.isInteger(m) &&
    y >= 1970 &&
    y <= 9999 &&
    m >= 1 &&
    m <= 12
  ) {
    return { year: y, month: m };
  }
  return defaultReferencePeriod(today);
}

export function useHighlights() {
  const [searchParams, setSearchParams] = useSearchParams();

  const rawRefY = searchParams.get("refY");
  const rawRefM = searchParams.get("refM");
  const { year: referenceYear, month: referenceMonth } = useMemo(
    () => resolveHighlightsReference(rawRefY, rawRefM),
    [rawRefY, rawRefM],
  );
  // YTD is always anchored on the current civil year — independent of the
  // user-picked reference month.
  const ytdYear = useMemo(() => new Date().getFullYear(), []);

  const [state, dispatch] = useReducer(reducer, initialState);
  const fetchIdRef = useRef(0);

  const fetch = useCallback(
    async (windowDays: 30 | 60 | 90, year: number, month: number, ytd: number) => {
      const id = ++fetchIdRef.current;
      dispatch({ type: "SET_LOADING", payload: true });
      try {
        const data = await getHighlights(year, month, ytd, windowDays);
        if (id !== fetchIdRef.current) return;
        dispatch({ type: "SET_DATA", payload: data });
      } catch (e) {
        if (id !== fetchIdRef.current) return;
        dispatch({ type: "SET_ERROR", payload: e instanceof Error ? e.message : String(e) });
      }
    },
    [],
  );

  useEffect(() => {
    fetch(state.windowDays, referenceYear, referenceMonth, ytdYear);
  }, [fetch, state.windowDays, referenceYear, referenceMonth, ytdYear]);

  const setWindowDays = useCallback((d: 30 | 60 | 90) => {
    dispatch({ type: "SET_WINDOW_DAYS", payload: d });
  }, []);

  const setReferencePeriod = useCallback(
    (year: number, month: number) => {
      setSearchParams(
        (prev) => {
          const params = new URLSearchParams(prev);
          params.set("refY", String(year));
          params.set("refM", String(month));
          return params;
        },
        { replace: true },
      );
    },
    [setSearchParams],
  );

  return {
    ...state,
    setWindowDays,
    year: referenceYear,
    month: referenceMonth,
    ytdYear,
    setReferencePeriod,
  };
}
