/**
 * Jails API module.
 *
 * Wraps all backend endpoints under `/api/jails`, `/api/bans`, and
 * `/api/geo` that relate to jail management.
 */

import { del, get, post } from "./client";
import { ENDPOINTS } from "./endpoints";
import type {
  ActiveBanListResponse,
  IpLookupResponse,
  JailBannedIpsResponse,
  JailCommandResponse,
  JailDetailResponse,
  JailListResponse,
  UnbanAllResponse,
} from "../types/jail";

// ---------------------------------------------------------------------------
// Jail overview
// ---------------------------------------------------------------------------

/**
 * Fetch the list of all fail2ban jails.
 *
 * @returns A {@link JailListResponse} containing summary info for each jail.
 * @throws {ApiError} On non-2xx responses.
 */
export async function fetchJails(): Promise<JailListResponse> {
  return get<JailListResponse>(ENDPOINTS.jails);
}

/**
 * Fetch full detail for a single jail.
 *
 * @param name - Jail name (e.g. `"sshd"`).
 * @returns A {@link JailDetailResponse} with config, ignore list, and status.
 * @throws {ApiError} On non-2xx responses (404 if the jail does not exist).
 */
export async function fetchJail(name: string): Promise<JailDetailResponse> {
  return get<JailDetailResponse>(ENDPOINTS.jail(name));
}

// ---------------------------------------------------------------------------
// Jail controls
// ---------------------------------------------------------------------------

/**
 * Start a stopped jail.
 *
 * @param name - Jail name.
 * @returns A {@link JailCommandResponse} confirming the operation.
 * @throws {ApiError} On non-2xx responses.
 */
export async function startJail(name: string): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailStart(name), {});
}

/**
 * Stop a running jail.
 *
 * @param name - Jail name.
 * @returns A {@link JailCommandResponse} confirming the operation.
 * @throws {ApiError} On non-2xx responses.
 */
export async function stopJail(name: string): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailStop(name), {});
}

/**
 * Toggle idle mode for a jail.
 *
 * @param name - Jail name.
 * @param on   - `true` to enable idle mode, `false` to disable.
 * @returns A {@link JailCommandResponse} confirming the toggle.
 * @throws {ApiError} On non-2xx responses.
 */
export async function setJailIdle(
  name: string,
  on: boolean,
): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailIdle(name), on);
}

/**
 * Reload configuration for a single jail.
 *
 * @param name - Jail name.
 * @returns A {@link JailCommandResponse} confirming the reload.
 * @throws {ApiError} On non-2xx responses.
 */
export async function reloadJail(name: string): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailReload(name), {});
}

/**
 * Reload configuration for **all** jails at once.
 *
 * @returns A {@link JailCommandResponse} confirming the operation.
 * @throws {ApiError} On non-2xx responses.
 */
export async function reloadAllJails(): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailsReloadAll, {});
}

// ---------------------------------------------------------------------------
// Ignore list
// ---------------------------------------------------------------------------

/**
 * Return the ignore list for a jail.
 *
 * @param name - Jail name.
 * @returns Array of IP addresses / CIDR networks on the ignore list.
 * @throws {ApiError} On non-2xx responses.
 */
export async function fetchIgnoreList(name: string): Promise<string[]> {
  return get<string[]>(ENDPOINTS.jailIgnoreIp(name));
}

/**
 * Add an IP or CIDR network to a jail's ignore list.
 *
 * @param name - Jail name.
 * @param ip   - IP address or CIDR network to add.
 * @returns A {@link JailCommandResponse} confirming the addition.
 * @throws {ApiError} On non-2xx responses.
 */
export async function addIgnoreIp(
  name: string,
  ip: string,
): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailIgnoreIp(name), { ip });
}

/**
 * Remove an IP or CIDR network from a jail's ignore list.
 *
 * @param name - Jail name.
 * @param ip   - IP address or CIDR network to remove.
 * @returns A {@link JailCommandResponse} confirming the removal.
 * @throws {ApiError} On non-2xx responses.
 */
export async function delIgnoreIp(
  name: string,
  ip: string,
): Promise<JailCommandResponse> {
  return del<JailCommandResponse>(ENDPOINTS.jailIgnoreIp(name), { ip });
}

/**
 * Enable or disable the `ignoreself` flag for a jail.
 *
 * When enabled, fail2ban automatically adds the server's own IP addresses to
 * the ignore list so the host can never ban itself.
 *
 * @param name - Jail name.
 * @param on   - `true` to enable, `false` to disable.
 * @returns A {@link JailCommandResponse} confirming the change.
 * @throws {ApiError} On non-2xx responses.
 */
export async function toggleIgnoreSelf(
  name: string,
  on: boolean,
): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.jailIgnoreSelf(name), on);
}

// ---------------------------------------------------------------------------
// Ban / unban
// ---------------------------------------------------------------------------

/**
 * Manually ban an IP address in a specific jail.
 *
 * @param jail - Jail name.
 * @param ip   - IP address to ban.
 * @returns A {@link JailCommandResponse} confirming the ban.
 * @throws {ApiError} On non-2xx responses.
 */
export async function banIp(
  jail: string,
  ip: string,
): Promise<JailCommandResponse> {
  return post<JailCommandResponse>(ENDPOINTS.bans, { jail, ip });
}

/**
 * Unban an IP address from a specific jail or all jails.
 *
 * @param ip       - IP address to unban.
 * @param jail     - Target jail name, or `undefined` to unban from all jails.
 * @param unbanAll - When `true`, remove the IP from every jail.
 * @returns A {@link JailCommandResponse} confirming the unban.
 * @throws {ApiError} On non-2xx responses.
 */
export async function unbanIp(
  ip: string,
  jail?: string,
  unbanAll = false,
): Promise<JailCommandResponse> {
  return del<JailCommandResponse>(ENDPOINTS.bans, { ip, jail, unban_all: unbanAll });
}

// ---------------------------------------------------------------------------
// Active bans
// ---------------------------------------------------------------------------

/**
 * Fetch all currently active bans across every jail.
 *
 * @returns An {@link ActiveBanListResponse} with geo-enriched entries.
 * @throws {ApiError} On non-2xx responses.
 */
export async function fetchActiveBans(): Promise<ActiveBanListResponse> {
  return get<ActiveBanListResponse>(ENDPOINTS.bansActive);
}

/**
 * Unban every currently banned IP across all jails in a single operation.
 *
 * Uses fail2ban's global `unban --all` command.
 *
 * @returns An {@link UnbanAllResponse} with the count of unbanned IPs.
 * @throws {ApiError} On non-2xx responses.
 */
export async function unbanAllBans(): Promise<UnbanAllResponse> {
  return del<UnbanAllResponse>(ENDPOINTS.bansAll);
}

// ---------------------------------------------------------------------------
// Geo / IP lookup
// ---------------------------------------------------------------------------

/**
 * Look up ban status and geo-location for an IP address.
 *
 * @param ip - IP address to look up.
 * @returns An {@link IpLookupResponse} with ban history and geo info.
 * @throws {ApiError} On non-2xx responses (400 for invalid IP).
 */
export async function lookupIp(ip: string): Promise<IpLookupResponse> {
  return get<IpLookupResponse>(ENDPOINTS.geoLookup(ip));
}

// ---------------------------------------------------------------------------
// Jail-specific paginated bans
// ---------------------------------------------------------------------------

/**
 * Fetch the currently banned IPs for a specific jail, paginated.
 *
 * Only the requested page is geo-enriched on the backend, so this call
 * remains fast even when a jail has thousands of banned IPs.
 *
 * @param jailName - Jail name (e.g. `"sshd"`).
 * @param page     - 1-based page number (default 1).
 * @param pageSize - Items per page; max 100 (default 25).
 * @param search   - Optional case-insensitive IP substring filter.
 * @returns A {@link JailBannedIpsResponse} with paginated ban entries.
 * @throws {ApiError} On non-2xx responses (404 if jail unknown, 502 if fail2ban down).
 */
export async function fetchJailBannedIps(
  jailName: string,
  page = 1,
  pageSize = 25,
  search?: string,
  signal?: AbortSignal,
): Promise<JailBannedIpsResponse> {
  const params: Record<string, string> = {
    page: String(page),
    page_size: String(pageSize),
  };
  if (search !== undefined && search !== "") {
    params.search = search;
  }
  const query = new URLSearchParams(params).toString();
  return get<JailBannedIpsResponse>(`${ENDPOINTS.jailBanned(jailName)}?${query}`, signal);
}
