import { useEffect, useMemo, useState } from "react";
import { getEventSummary } from "@/lib/features/event-analytics/query";
import type {
  EventAnalyticsFilters,
  EventSummary,
} from "@/lib/features/event-analytics/types";

const DAY_MS = 24 * 60 * 60 * 1000;

/** `yyyy-mm-dd` in local time — the format the filters and core both use. */
const toDateParam = (d: Date) => {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
};

/**
 * The window of equal length immediately before the selected one.
 *
 * Immediately before, not the same dates a month earlier: the comparison is
 * "against the last N days", so it has to be contiguous with the range and the
 * same length, or the two figures are not counting the same amount of time.
 *
 * Returns null unless both bounds are set. An open-ended range has no length,
 * so there is nothing to shift and nothing honest to compare against.
 */
export function previousPeriod(
  filters: EventAnalyticsFilters,
): { from: string; to: string; days: number } | null {
  if (!filters.from || !filters.to) return null;

  const from = new Date(`${filters.from}T00:00:00`);
  const to = new Date(`${filters.to}T00:00:00`);
  if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) return null;
  if (to < from) return null;

  // Inclusive of both ends: 1 Jan to 1 Jan is one day, not zero.
  const days = Math.round((to.getTime() - from.getTime()) / DAY_MS) + 1;

  const prevTo = new Date(from.getTime() - DAY_MS);
  const prevFrom = new Date(prevTo.getTime() - (days - 1) * DAY_MS);

  return { from: toDateParam(prevFrom), to: toDateParam(prevTo), days };
}

/** Percentage change, or null when there is no earlier figure to divide by. */
export function pctChange(current: number, previous: number): number | null {
  if (!previous) return null;
  return ((current - previous) / previous) * 100;
}

export type ComparePeriod = {
  /** The earlier window's figures, or null when the range is open-ended. */
  summary: EventSummary | null;
  /** "vs previous 28 days" — ready to sit beside a delta. */
  label: string | null;
  /** The dates being compared against, for a caption under the date filter. */
  range: { from: string; to: string; days: number } | null;
  loading: boolean;
};

/**
 * Figures for the period before the selected one, for period-over-period deltas.
 *
 * A second request rather than a core change: the endpoint already accepts an
 * arbitrary range, so the comparison is the same query with shifted bounds and
 * needs no new server code, no new cache key shape and no migration. Every
 * other filter is carried across unchanged, so "Instagram carts, this fortnight
 * vs last" compares like with like.
 */
export function useComparePeriod(
  filters: EventAnalyticsFilters,
  /** Skip the request when the caller has nothing to show it on. */
  enabled = true,
): ComparePeriod {
  const range = useMemo(() => previousPeriod(filters), [filters]);
  const [summary, setSummary] = useState<EventSummary | null>(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!enabled || !range) {
      setSummary(null);
      return;
    }

    const controller = new AbortController();
    setLoading(true);

    (async () => {
      try {
        const res = await getEventSummary(
          { ...filters, from: range.from, to: range.to },
          { signal: controller.signal },
        );
        setSummary(res.data?.summary ?? null);
      } catch {
        /*
         * A failed comparison must not break the page.
         *
         * The deltas are an annotation on figures that are already correct, so
         * dropping them silently leaves a dashboard that is merely less
         * informative, where surfacing the error would suggest the numbers
         * beside it are suspect.
         */
        setSummary(null);
      } finally {
        if (!controller.signal.aborted) setLoading(false);
      }
    })();

    return () => controller.abort();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled, range?.from, range?.to, JSON.stringify({ ...filters, from: null, to: null })]);

  return {
    summary,
    label: range ? `vs previous ${range.days} days` : null,
    range,
    loading,
  };
}
