import { useEffect, useState } from "react";
import type { ApiClient } from "@/shared/api/client.js";
import { useApiClient } from "@/shared/api/provider.js";
import { getAppSettings, type AppSettingsResponse } from "@/shared/api/appSettings.js";

// Module-level single-flight cache so 7+ consumer components don't
// each GET /appSettings independently on the same page.
const settingsCache = new WeakMap<ApiClient, Promise<AppSettingsResponse>>();

function getSettings(client: ApiClient): Promise<AppSettingsResponse> {
  const existing = settingsCache.get(client);
  if (existing) return existing;
  const promise = getAppSettings(client);
  settingsCache.set(client, promise);
  return promise;
}

const DAYS_PATTERN = /^(\d+)d$/;
const HOURS_PATTERN = /^(\d+)h$/;

// Mirrors Angular's `AppSettings` default payload
// (ClientApp/src/app/shared/models-legacy/app-settings.model.ts):
//   boardSearchFrom: 1, boardSearchTo: 7,
//   scheduleSearchFrom: 1, scheduleSearchTo: 330,
//   flightStatusAvailableFrom: 2 (hours), buyPeriod.min: 2h, buyPeriod.max: 330d
// `buyTicketMaxHours` mirrors Angular's `330d` → 330 * 24 = 7920 hours;
// without this the Buy button hides for any flight more than ~3 days
// out, which made the schedule details page look like 'no Купить
// button' for the typical search-2-weeks-ahead flow.
const DEFAULTS = {
  onlineboardSearchFrom: 1,
  onlineboardSearchTo: 7,
  scheduleSearchFrom: 1,
  scheduleSearchTo: 330,
  flightStatusAvailableFromHours: 2,
  buyTicketMinHours: 2,
  buyTicketMaxHours: 330 * 24,
} as const;

function parsePattern(
  value: string | undefined,
  pattern: RegExp,
  fallback: number,
): number {
  if (!value) return fallback;
  const match = pattern.exec(value);
  if (!match?.[1]) return fallback;
  return parseInt(match[1], 10);
}

function parseDays(value: string | undefined, fallback: number): number {
  return parsePattern(value, DAYS_PATTERN, fallback);
}

function parseHours(value: string | undefined, fallback: number): number {
  return parsePattern(value, HOURS_PATTERN, fallback);
}

/** Parse an `<n>h` or `<n>d` duration into hours. Days expand to 24×n.
 *  Used for `buyPeriod.max` which Angular's settings API returns as
 *  `"330d"` (any other consumer in the React codebase that needs to
 *  accept either unit should use this rather than parseHours). */
function parseHoursOrDays(value: string | undefined, fallback: number): number {
  if (!value) return fallback;
  const hMatch = HOURS_PATTERN.exec(value);
  if (hMatch?.[1]) return parseInt(hMatch[1], 10);
  const dMatch = DAYS_PATTERN.exec(value);
  if (dMatch?.[1]) return parseInt(dMatch[1], 10) * 24;
  return fallback;
}

export interface UseAppSettingsResult {
  onlineboardSearchFrom: number;
  onlineboardSearchTo: number;
  scheduleSearchFrom: number;
  scheduleSearchTo: number;
  flightStatusAvailableFromHours: number;
  buyTicketMinHours: number;
  buyTicketMaxHours: number;
  loading: boolean;
  error: Error | null;
}

/**
 * Fetches the global app settings and exposes day-range and button-config numbers.
 * On error or parse failure, returns defaults.
 */
export function useAppSettings(): UseAppSettingsResult {
  const client = useApiClient();
  const [state, setState] = useState<Omit<UseAppSettingsResult, "loading" | "error">>(DEFAULTS);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    getSettings(client)
      .then((response) => {
        if (cancelled) return;
        const ob = response.uiOptions?.filter?.onlineboard;
        const sc = response.uiOptions?.filter?.schedule;
        const fs = response.uiOptions?.buttons?.flightStatus;
        const bt = response.uiOptions?.buttons?.buyTicket;

        setState({
          onlineboardSearchFrom: parseDays(ob?.searchFrom, DEFAULTS.onlineboardSearchFrom),
          onlineboardSearchTo: parseDays(ob?.searchTo, DEFAULTS.onlineboardSearchTo),
          scheduleSearchFrom: parseDays(sc?.searchFrom, DEFAULTS.scheduleSearchFrom),
          scheduleSearchTo: parseDays(sc?.searchTo, DEFAULTS.scheduleSearchTo),
          flightStatusAvailableFromHours: parseHours(
            fs?.availableFrom,
            DEFAULTS.flightStatusAvailableFromHours,
          ),
          buyTicketMinHours: parseHoursOrDays(bt?.period?.min, DEFAULTS.buyTicketMinHours),
          buyTicketMaxHours: parseHoursOrDays(bt?.period?.max, DEFAULTS.buyTicketMaxHours),
        });
        setLoading(false);
      })
      .catch((err: Error) => {
        if (cancelled) return;
        setError(err);
        setLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, [client]);

  return { ...state, loading, error };
}
