/**
 * React hooks for the configuration and server settings data.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import {
  addLogPath,
  fetchGlobalConfig,
  fetchJailConfig,
  fetchJailConfigs,
  fetchServerSettings,
  flushLogs,
  previewLog,
  reloadConfig,
  restartFail2Ban,
  testRegex,
  updateGlobalConfig,
  updateJailConfig,
  updateServerSettings,
} from "../api/config";
import { handleFetchError } from "../utils/fetchError";
import type {
  AddLogPathRequest,
  GlobalConfig,
  GlobalConfigUpdate,
  JailConfig,
  JailConfigUpdate,
  LogPreviewRequest,
  LogPreviewResponse,
  RegexTestRequest,
  RegexTestResponse,
  ServerSettings,
  ServerSettingsUpdate,
} from "../types/config";

// ---------------------------------------------------------------------------
// useJailConfigs — list all jail configs
// ---------------------------------------------------------------------------

interface UseJailConfigsResult {
  jails: JailConfig[];
  total: number;
  loading: boolean;
  error: string | null;
  refresh: () => void;
  updateJail: (name: string, update: JailConfigUpdate) => Promise<void>;
  reloadAll: () => Promise<void>;
}

export function useJailConfigs(): UseJailConfigsResult {
  const [jails, setJails] = useState<JailConfig[]>([]);
  const [total, setTotal] = useState(0);
  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);

    fetchJailConfigs()
      .then((resp) => {
        setJails(resp.jails);
        setTotal(resp.total);
      })
      .catch((err: unknown) => {
        handleFetchError(err, setError, "Failed to fetch jail configs");
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

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

  const updateJail = useCallback(
    async (name: string, update: JailConfigUpdate): Promise<void> => {
      await updateJailConfig(name, update);
      load();
    },
    [load],
  );

  const reloadAll = useCallback(async (): Promise<void> => {
    await reloadConfig();
    load();
  }, [load]);

  return { jails, total, loading, error, refresh: load, updateJail, reloadAll };
}

// ---------------------------------------------------------------------------
// useJailConfigDetail — single jail config with mutation
// ---------------------------------------------------------------------------

interface UseJailConfigDetailResult {
  jail: JailConfig | null;
  loading: boolean;
  error: string | null;
  refresh: () => void;
  updateJail: (update: JailConfigUpdate) => Promise<void>;
  addLog: (req: AddLogPathRequest) => Promise<void>;
}

export function useJailConfigDetail(name: string): UseJailConfigDetailResult {
  const [jail, setJail] = useState<JailConfig | null>(null);
  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);

    fetchJailConfig(name)
      .then((resp) => {
        setJail(resp.jail);
      })
      .catch((err: unknown) => {
        handleFetchError(err, setError, "Failed to fetch jail config");
      })
      .finally(() => {
        setLoading(false);
      });
  }, [name]);

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

  const updateJail = useCallback(
    async (update: JailConfigUpdate): Promise<void> => {
      await updateJailConfig(name, update);
      load();
    },
    [name, load],
  );

  const addLog = useCallback(
    async (req: AddLogPathRequest): Promise<void> => {
      await addLogPath(name, req);
      load();
    },
    [name, load],
  );

  return { jail, loading, error, refresh: load, updateJail, addLog };
}

// ---------------------------------------------------------------------------
// useGlobalConfig
// ---------------------------------------------------------------------------

interface UseGlobalConfigResult {
  config: GlobalConfig | null;
  loading: boolean;
  error: string | null;
  refresh: () => void;
  updateConfig: (update: GlobalConfigUpdate) => Promise<void>;
}

export function useGlobalConfig(): UseGlobalConfigResult {
  const [config, setConfig] = useState<GlobalConfig | null>(null);
  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);

    fetchGlobalConfig()
      .then(setConfig)
      .catch((err: unknown) => {
        handleFetchError(err, setError, "Failed to fetch global config");
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

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

  const updateConfig = useCallback(
    async (update: GlobalConfigUpdate): Promise<void> => {
      await updateGlobalConfig(update);
      load();
    },
    [load],
  );

  return { config, loading, error, refresh: load, updateConfig };
}

// ---------------------------------------------------------------------------
// useServerSettings
// ---------------------------------------------------------------------------

interface UseServerSettingsResult {
  settings: ServerSettings | null;
  loading: boolean;
  error: string | null;
  refresh: () => void;
  updateSettings: (update: ServerSettingsUpdate) => Promise<void>;
  flush: () => Promise<string>;
  reload: () => Promise<void>;
  restart: () => Promise<void>;
}

export function useServerSettings(): UseServerSettingsResult {
  const [settings, setSettings] = useState<ServerSettings | null>(null);
  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);

    fetchServerSettings()
      .then((resp) => {
        setSettings(resp.settings);
      })
      .catch((err: unknown) => {
        handleFetchError(err, setError, "Failed to fetch server settings");
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

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

  const updateSettings_ = useCallback(
    async (update: ServerSettingsUpdate): Promise<void> => {
      await updateServerSettings(update);
      load();
    },
    [load],
  );

  const reload = useCallback(async (): Promise<void> => {
    await reloadConfig();
    load();
  }, [load]);

  const restart = useCallback(async (): Promise<void> => {
    await restartFail2Ban();
    load();
  }, [load]);

  const flush = useCallback(async (): Promise<string> => {
    return flushLogs();
  }, []);

  return {
    settings,
    loading,
    error,
    refresh: load,
    updateSettings: updateSettings_,
    flush,
    reload,
    restart,
  };
}

// ---------------------------------------------------------------------------
// useRegexTester — lazy, triggered by test(req)
// ---------------------------------------------------------------------------

interface UseRegexTesterResult {
  result: RegexTestResponse | null;
  testing: boolean;
  test: (req: RegexTestRequest) => Promise<void>;
}

export function useRegexTester(): UseRegexTesterResult {
  const [result, setResult] = useState<RegexTestResponse | null>(null);
  const [testing, setTesting] = useState(false);

  const test_ = useCallback(async (req: RegexTestRequest): Promise<void> => {
    setTesting(true);
    try {
      const resp = await testRegex(req);
      setResult(resp);
    } catch (err: unknown) {
      if (err instanceof Error) {
        setResult({ matched: false, groups: [], error: err.message });
      }
    } finally {
      setTesting(false);
    }
  }, []);

  return { result, testing, test: test_ };
}

// ---------------------------------------------------------------------------
// useLogPreview — lazy, triggered by preview(req)
// ---------------------------------------------------------------------------

interface UseLogPreviewResult {
  preview: LogPreviewResponse | null;
  loading: boolean;
  run: (req: LogPreviewRequest) => Promise<void>;
}

export function useLogPreview(): UseLogPreviewResult {
  const [preview, setPreview] = useState<LogPreviewResponse | null>(null);
  const [loading, setLoading] = useState(false);

  const run_ = useCallback(async (req: LogPreviewRequest): Promise<void> => {
    setLoading(true);
    try {
      const resp = await previewLog(req);
      setPreview(resp);
    } catch (err: unknown) {
      if (err instanceof Error) {
        setPreview({
          lines: [],
          total_lines: 0,
          matched_count: 0,
          regex_error: err.message,
        });
      }
    } finally {
      setLoading(false);
    }
  }, []);

  return { preview, loading, run: run_ };
}
