/**
 * React hooks for blocklist management data fetching.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import {
  createBlocklist,
  deleteBlocklist,
  fetchBlocklists,
  fetchImportLog,
  fetchSchedule,
  previewBlocklist,
  runImportNow,
  updateBlocklist,
  updateSchedule,
} from "../api/blocklist";
import { handleFetchError } from "../utils/fetchError";
import type {
  BlocklistSource,
  BlocklistSourceCreate,
  BlocklistSourceUpdate,
  ImportLogListResponse,
  ImportRunResult,
  PreviewResponse,
  ScheduleConfig,
  ScheduleInfo,
} from "../types/blocklist";

// ---------------------------------------------------------------------------
// useBlocklists
// ---------------------------------------------------------------------------

export interface UseBlocklistsReturn {
  sources: BlocklistSource[];
  loading: boolean;
  error: string | null;
  refresh: () => void;
  createSource: (payload: BlocklistSourceCreate) => Promise<BlocklistSource>;
  updateSource: (id: number, payload: BlocklistSourceUpdate) => Promise<BlocklistSource>;
  removeSource: (id: number) => Promise<void>;
  previewSource: (id: number) => Promise<PreviewResponse>;
}

/**
 * Load all blocklist sources and expose CRUD operations.
 */
export function useBlocklists(): UseBlocklistsReturn {
  const [sources, setSources] = useState<BlocklistSource[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  const load = useCallback((): void => {
    abortRef.current?.abort();
    const ctrl = new AbortController();
    abortRef.current = ctrl;

    setLoading(true);
    setError(null);

    fetchBlocklists()
      .then((data) => {
        if (!ctrl.signal.aborted) {
          setSources(data.sources);
          setLoading(false);
        }
      })
      .catch((err: unknown) => {
        if (!ctrl.signal.aborted) {
          handleFetchError(err, setError, "Failed to load blocklists");
          setLoading(false);
        }
      });
  }, []);

  useEffect(() => {
    load();
    return (): void => {
      abortRef.current?.abort();
    };
  }, [load]);

  const createSource = useCallback(
    async (payload: BlocklistSourceCreate): Promise<BlocklistSource> => {
      const created = await createBlocklist(payload);
      setSources((prev) => [...prev, created]);
      return created;
    },
    [],
  );

  const updateSource = useCallback(
    async (id: number, payload: BlocklistSourceUpdate): Promise<BlocklistSource> => {
      const updated = await updateBlocklist(id, payload);
      setSources((prev) => prev.map((s) => (s.id === id ? updated : s)));
      return updated;
    },
    [],
  );

  const removeSource = useCallback(async (id: number): Promise<void> => {
    await deleteBlocklist(id);
    setSources((prev) => prev.filter((s) => s.id !== id));
  }, []);

  const previewSource = useCallback(async (id: number): Promise<PreviewResponse> => {
    return previewBlocklist(id);
  }, []);

  return {
    sources,
    loading,
    error,
    refresh: load,
    createSource,
    updateSource,
    removeSource,
    previewSource,
  };
}

// ---------------------------------------------------------------------------
// useSchedule
// ---------------------------------------------------------------------------

export interface UseScheduleReturn {
  info: ScheduleInfo | null;
  loading: boolean;
  error: string | null;
  saveSchedule: (config: ScheduleConfig) => Promise<void>;
}

/**
 * Fetch and update the blocklist import schedule.
 */
export function useSchedule(): UseScheduleReturn {
  const [info, setInfo] = useState<ScheduleInfo | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setLoading(true);
    fetchSchedule()
      .then((data) => {
        setInfo(data);
        setLoading(false);
      })
      .catch((err: unknown) => {
        handleFetchError(err, setError, "Failed to load schedule");
        setLoading(false);
      });
  }, []);

  const saveSchedule = useCallback(async (config: ScheduleConfig): Promise<void> => {
    const updated = await updateSchedule(config);
    setInfo(updated);
  }, []);

  return { info, loading, error, saveSchedule };
}

// ---------------------------------------------------------------------------
// useImportLog
// ---------------------------------------------------------------------------

export interface UseImportLogReturn {
  data: ImportLogListResponse | null;
  loading: boolean;
  error: string | null;
  page: number;
  setPage: (n: number) => void;
  refresh: () => void;
}

/**
 * Fetch the paginated import log with optional source filter.
 */
export function useImportLog(
  sourceId?: number,
  pageSize = 50,
): UseImportLogReturn {
  const [data, setData] = useState<ImportLogListResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [page, setPage] = useState(1);
  const abortRef = useRef<AbortController | null>(null);

  const load = useCallback((): void => {
    abortRef.current?.abort();
    const ctrl = new AbortController();
    abortRef.current = ctrl;

    setLoading(true);
    setError(null);

    fetchImportLog(page, pageSize, sourceId)
      .then((result) => {
        if (!ctrl.signal.aborted) {
          setData(result);
          setLoading(false);
        }
      })
      .catch((err: unknown) => {
        if (!ctrl.signal.aborted) {
          handleFetchError(err, setError, "Failed to load import log");
          setLoading(false);
        }
      });
  }, [page, pageSize, sourceId]);

  useEffect(() => {
    load();
    return (): void => {
      abortRef.current?.abort();
    };
  }, [load]);

  return { data, loading, error, page, setPage, refresh: load };
}

// ---------------------------------------------------------------------------
// useRunImport
// ---------------------------------------------------------------------------

export interface UseRunImportReturn {
  running: boolean;
  lastResult: ImportRunResult | null;
  error: string | null;
  runNow: () => Promise<void>;
}

/**
 * Trigger and track a manual blocklist import run.
 */
export function useRunImport(): UseRunImportReturn {
  const [running, setRunning] = useState(false);
  const [lastResult, setLastResult] = useState<ImportRunResult | null>(null);
  const [error, setError] = useState<string | null>(null);

  const runNow = useCallback(async (): Promise<void> => {
    setRunning(true);
    setError(null);
    try {
      const result = await runImportNow();
      setLastResult(result);
    } catch (err: unknown) {
      handleFetchError(err, setError, "Import failed");
    } finally {
      setRunning(false);
    }
  }, []);

  return { running, lastResult, error, runNow };
}

// ---------------------------------------------------------------------------
// useBlocklistStatus
// ---------------------------------------------------------------------------

/** How often to re-check the schedule endpoint for import errors (ms). */
const BLOCKLIST_POLL_INTERVAL_MS = 60_000;

export interface UseBlocklistStatusReturn {
  /** `true` when the most recent import run completed with errors. */
  hasErrors: boolean;
}

/**
 * Poll `GET /api/blocklists/schedule` every 60 seconds to detect whether
 * the most recent blocklist import had errors.
 *
 * Network failures during polling are silently ignored — the indicator
 * simply retains its previous value until the next successful poll.
 */
export function useBlocklistStatus(): UseBlocklistStatusReturn {
  const [hasErrors, setHasErrors] = useState(false);

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

    const poll = (): void => {
      fetchSchedule()
        .then((info) => {
          if (!cancelled) {
            setHasErrors(info.last_run_errors === true);
          }
        })
        .catch(() => {
          // Silently swallow network errors — do not change indicator state.
        });
    };

    poll();
    const id = window.setInterval(poll, BLOCKLIST_POLL_INTERVAL_MS);
    return (): void => {
      cancelled = true;
      window.clearInterval(id);
    };
  }, []);

  return { hasErrors };
}
