/**
 * API functions for the configuration endpoints.
 */

import { del, get, post, put } from "./client";
import { ENDPOINTS } from "./endpoints";
import type {
  ActionConfig,
  ActionConfigUpdate,
  ActionCreateRequest,
  ActionListResponse,
  ActionUpdateRequest,
  ActivateJailRequest,
  AddLogPathRequest,
  AssignActionRequest,
  AssignFilterRequest,
  ConfFileContent,
  ConfFileCreateRequest,
  ConfFilesResponse,
  ConfFileUpdateRequest,
  Fail2BanLogResponse,
  FilterConfig,
  FilterConfigUpdate,
  FilterCreateRequest,
  FilterListResponse,
  FilterUpdateRequest,
  GlobalConfig,
  GlobalConfigUpdate,
  InactiveJailListResponse,
  JailActivationResponse,
  JailConfigFileContent,
  JailConfigFileEnabledUpdate,
  JailConfigFilesResponse,
  JailConfigListResponse,
  JailConfigResponse,
  JailConfigUpdate,
  JailValidationResult,
  LogPreviewRequest,
  LogPreviewResponse,
  MapColorThresholdsResponse,
  MapColorThresholdsUpdate,
  RegexTestRequest,
  RegexTestResponse,
  JailFileConfig,
  JailFileConfigUpdate,
  ServiceStatusResponse,
} from "../types/config";

// ---------------------------------------------------------------------------
// Jail configuration
// ---------------------------------------------------------------------------

export async function fetchJailConfigs(
): Promise<JailConfigListResponse> {
  return get<JailConfigListResponse>(ENDPOINTS.configJails);
}

export async function fetchJailConfig(
  name: string
): Promise<JailConfigResponse> {
  return get<JailConfigResponse>(ENDPOINTS.configJail(name));
}

export async function updateJailConfig(
  name: string,
  update: JailConfigUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configJail(name), update);
}

// ---------------------------------------------------------------------------
// Global configuration
// ---------------------------------------------------------------------------

export async function fetchGlobalConfig(
): Promise<GlobalConfig> {
  return get<GlobalConfig>(ENDPOINTS.configGlobal);
}

export async function updateGlobalConfig(
  update: GlobalConfigUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configGlobal, update);
}

// ---------------------------------------------------------------------------
// Reload and Restart
// ---------------------------------------------------------------------------

export async function reloadConfig(
): Promise<void> {
  await post<undefined>(ENDPOINTS.configReload, undefined);
}

export async function restartFail2Ban(
): Promise<void> {
  await post<undefined>(ENDPOINTS.configRestart, undefined);
}

// ---------------------------------------------------------------------------
// Regex tester
// ---------------------------------------------------------------------------

export async function testRegex(
  req: RegexTestRequest
): Promise<RegexTestResponse> {
  return post<RegexTestResponse>(ENDPOINTS.configRegexTest, req);
}

// ---------------------------------------------------------------------------
// Log path management
// ---------------------------------------------------------------------------

export async function addLogPath(
  jailName: string,
  req: AddLogPathRequest
): Promise<void> {
  await post<undefined>(ENDPOINTS.configJailLogPath(jailName), req);
}

export async function deleteLogPath(
  jailName: string,
  logPath: string
): Promise<void> {
  await del<undefined>(
    `${ENDPOINTS.configJailLogPath(jailName)}?log_path=${encodeURIComponent(logPath)}`
  );
}

// ---------------------------------------------------------------------------
// Log preview
// ---------------------------------------------------------------------------

export async function previewLog(
  req: LogPreviewRequest
): Promise<LogPreviewResponse> {
  return post<LogPreviewResponse>(ENDPOINTS.configPreviewLog, req);
}

// ---------------------------------------------------------------------------
// Map color thresholds
// ---------------------------------------------------------------------------

export async function fetchMapColorThresholds(
signal?: AbortSignal,
): Promise<MapColorThresholdsResponse> {
  return get<MapColorThresholdsResponse>(ENDPOINTS.configMapColorThresholds, signal);
}

export async function updateMapColorThresholds(
  update: MapColorThresholdsUpdate
): Promise<MapColorThresholdsResponse> {
  return put<MapColorThresholdsResponse>(
    ENDPOINTS.configMapColorThresholds,
    update,
  );
}

// ---------------------------------------------------------------------------
// Jail config files (Task 4a)
// ---------------------------------------------------------------------------

export async function fetchJailConfigFiles(): Promise<JailConfigFilesResponse> {
  return get<JailConfigFilesResponse>(ENDPOINTS.configJailFiles);
}

export async function createJailConfigFile(
  req: ConfFileCreateRequest
): Promise<ConfFileContent> {
  return post<ConfFileContent>(ENDPOINTS.configJailFiles, req);
}

export async function fetchJailConfigFileContent(
  filename: string
): Promise<JailConfigFileContent> {
  return get<JailConfigFileContent>(ENDPOINTS.configJailFile(filename));
}

export async function updateJailConfigFile(
  filename: string,
  req: ConfFileUpdateRequest
): Promise<void> {
  await put<undefined>(ENDPOINTS.configJailFile(filename), req);
}

export async function setJailConfigFileEnabled(
  filename: string,
  update: JailConfigFileEnabledUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configJailFileEnabled(filename), update);
}

// ---------------------------------------------------------------------------
// Filter files (Task 4d) — raw file management
// ---------------------------------------------------------------------------

/**
 * Return a lightweight name/filename list of all filter files.
 *
 * Internally calls the enriched ``GET /config/filters`` endpoint (which also
 * returns active-status data) and maps the result down to the simpler
 * ``ConfFilesResponse`` shape expected by the raw-file editor and export tab.
 */
export async function fetchFilterFiles(): Promise<ConfFilesResponse> {
  const result = await fetchFilters();
  return {
    files: result.filters.map((f) => ({ name: f.name, filename: f.filename })),
    total: result.total,
  };
}

/** Fetch the raw content of a filter definition file for the raw editor. */
export async function fetchFilterFile(name: string): Promise<ConfFileContent> {
  return get<ConfFileContent>(ENDPOINTS.configFilterRaw(name));
}

/** Save raw content to a filter definition file (``PUT /filters/{name}/raw``). */
export async function updateFilterFile(
  name: string,
  req: ConfFileUpdateRequest
): Promise<void> {
  await put<undefined>(ENDPOINTS.configFilterRaw(name), req);
}

/** Create a new raw filter file (``POST /filters/raw``). */
export async function createFilterFile(
  req: ConfFileCreateRequest
): Promise<ConfFileContent> {
  return post<ConfFileContent>(ENDPOINTS.configFiltersRaw, req);
}

// ---------------------------------------------------------------------------
// Action files (Task 4e)
// ---------------------------------------------------------------------------

export async function fetchActionFiles(): Promise<ConfFilesResponse> {
  return get<ConfFilesResponse>(ENDPOINTS.configActions);
}

export async function fetchActionFile(name: string): Promise<ConfFileContent> {
  return get<ConfFileContent>(ENDPOINTS.configActionRaw(name));
}

export async function updateActionFile(
  name: string,
  req: ConfFileUpdateRequest
): Promise<void> {
  await put<undefined>(ENDPOINTS.configActionRaw(name), req);
}

export async function createActionFile(
  req: ConfFileCreateRequest
): Promise<ConfFileContent> {
  return post<ConfFileContent>(ENDPOINTS.configActions, req);
}

// ---------------------------------------------------------------------------
// Parsed filter config (Task 2.2 / legacy /parsed endpoint)
// ---------------------------------------------------------------------------

export async function fetchParsedFilter(name: string, signal?: AbortSignal): Promise<FilterConfig> {
  return get<FilterConfig>(ENDPOINTS.configFilterParsed(name), signal);
}

export async function updateParsedFilter(
  name: string,
  update: FilterConfigUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configFilterParsed(name), update);
}

// ---------------------------------------------------------------------------
// Filter structured update / create / delete (Task 2.3)
// ---------------------------------------------------------------------------

/**
 * Update a filter's editable fields via the structured endpoint.
 *
 * Writes only the supplied fields to the ``.local`` override.  Fields set
 * to ``null`` are cleared; omitted fields are left unchanged.
 *
 * @param name - Filter base name (e.g. ``"sshd"``)
 * @param req - Partial update payload.
 */
export async function updateFilter(
  name: string,
  req: FilterUpdateRequest
): Promise<void> {
  await put<undefined>(ENDPOINTS.configFilter(name), req);
}

/**
 * Create a brand-new user-defined filter in ``filter.d/{name}.local``.
 *
 * @param req - Name and optional regex patterns.
 * @returns The newly created FilterConfig.
 */
export async function createFilter(
  req: FilterCreateRequest
): Promise<FilterConfig> {
  return post<FilterConfig>(ENDPOINTS.configFilters, req);
}

/**
 * Delete a filter's ``.local`` override file.
 *
 * Only custom ``.local``-only filters can be deleted.  Attempting to delete a
 * filter that is backed by a shipped ``.conf`` file returns 409.
 *
 * @param name - Filter base name.
 */
export async function deleteFilter(name: string): Promise<void> {
  await del<undefined>(ENDPOINTS.configFilter(name));
}

/**
 * Assign a filter to a jail by writing ``filter = {filter_name}`` to the
 * jail's ``.local`` config file.
 *
 * @param jailName - Jail name.
 * @param req - The filter to assign.
 * @param reload - When ``true``, trigger a fail2ban reload after writing.
 */
export async function assignFilterToJail(
  jailName: string,
  req: AssignFilterRequest,
  reload = false
): Promise<void> {
  const url = reload
    ? `${ENDPOINTS.configJailFilter(jailName)}?reload=true`
    : ENDPOINTS.configJailFilter(jailName);
  await post<undefined>(url, req);
}

// ---------------------------------------------------------------------------
// Filter discovery with active/inactive status (Task 2.1)
// ---------------------------------------------------------------------------

/**
 * Fetch all filters from filter.d/ with active/inactive status.
 *
 * Active filters (those referenced by running jails) are returned first,
 * followed by inactive ones.  Both groups are sorted alphabetically.
 *
 * @returns FilterListResponse with all discovered filters and status.
 */
export async function fetchFilters(): Promise<FilterListResponse> {
  return get<FilterListResponse>(ENDPOINTS.configFilters);
}

/**
 * Fetch full parsed detail for a single filter with active/inactive status.
 *
 * @param name - Filter base name (e.g. "sshd" or "sshd.conf").
 * @returns FilterConfig with active, used_by_jails, source_file populated.
 */
export async function fetchFilter(name: string): Promise<FilterConfig> {
  return get<FilterConfig>(ENDPOINTS.configFilter(name));
}

// ---------------------------------------------------------------------------
// Parsed action config (Task 3.2)
// ---------------------------------------------------------------------------

export async function fetchParsedAction(name: string): Promise<ActionConfig> {
  return get<ActionConfig>(ENDPOINTS.configActionParsed(name));
}

export async function updateParsedAction(
  name: string,
  update: ActionConfigUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configActionParsed(name), update);
}

// ---------------------------------------------------------------------------
// Action discovery with active/inactive status (Task 3.1 / 3.2)
// ---------------------------------------------------------------------------

/**
 * Fetch all actions from action.d/ with active/inactive status.
 *
 * Active actions (those referenced by running jails) are returned first,
 * followed by inactive ones.  Both groups are sorted alphabetically.
 *
 * @returns ActionListResponse with all discovered actions and status.
 */
export async function fetchActions(): Promise<ActionListResponse> {
  return get<ActionListResponse>(ENDPOINTS.configActions);
}

/**
 * Fetch full parsed detail for a single action.
 *
 * @param name - Action base name (e.g. "iptables" or "iptables.conf").
 * @returns ActionConfig with active, used_by_jails, source_file populated.
 */
export async function fetchAction(name: string, signal?: AbortSignal): Promise<ActionConfig> {
  return get<ActionConfig>(ENDPOINTS.configAction(name), signal);
}

/**
 * Update an action's editable fields via the structured endpoint.
 *
 * Writes only the supplied fields to the ``.local`` override.  Fields set
 * to ``null`` are cleared; omitted fields are left unchanged.
 *
 * @param name - Action base name (e.g. ``"iptables"``)
 * @param req - Partial update payload.
 */
export async function updateAction(
  name: string,
  req: ActionUpdateRequest
): Promise<void> {
  await put<undefined>(ENDPOINTS.configAction(name), req);
}

/**
 * Create a brand-new user-defined action in ``action.d/{name}.local``.
 *
 * @param req - Name and optional lifecycle commands.
 * @returns The newly created ActionConfig.
 */
export async function createAction(
  req: ActionCreateRequest
): Promise<ActionConfig> {
  return post<ActionConfig>(ENDPOINTS.configActions, req);
}

/**
 * Delete an action's ``.local`` override file.
 *
 * Only custom ``.local``-only actions can be deleted.  Attempting to delete an
 * action backed by a shipped ``.conf`` file returns 409.
 *
 * @param name - Action base name.
 */
export async function deleteAction(name: string): Promise<void> {
  await del<undefined>(ENDPOINTS.configAction(name));
}

/**
 * Assign an action to a jail by appending it to the jail's action list.
 *
 * @param jailName - Jail name.
 * @param req - The action to assign with optional parameters.
 * @param reload - When ``true``, trigger a fail2ban reload after writing.
 */
export async function assignActionToJail(
  jailName: string,
  req: AssignActionRequest,
  reload = false
): Promise<void> {
  const url = reload
    ? `${ENDPOINTS.configJailAction(jailName)}?reload=true`
    : ENDPOINTS.configJailAction(jailName);
  await post<undefined>(url, req);
}

/**
 * Remove an action from a jail's action list.
 *
 * @param jailName - Jail name.
 * @param actionName - Action base name to remove.
 * @param reload - When ``true``, trigger a fail2ban reload after writing.
 */
export async function removeActionFromJail(
  jailName: string,
  actionName: string,
  reload = false
): Promise<void> {
  const url = reload
    ? `${ENDPOINTS.configJailActionName(jailName, actionName)}?reload=true`
    : ENDPOINTS.configJailActionName(jailName, actionName);
  await del<undefined>(url);
}

// ---------------------------------------------------------------------------
// Parsed jail file config (Task 6.1 / 6.2)
// ---------------------------------------------------------------------------

export async function fetchParsedJailFile(filename: string, signal?: AbortSignal): Promise<JailFileConfig> {
  return get<JailFileConfig>(ENDPOINTS.configJailFileParsed(filename), signal);
}

export async function updateParsedJailFile(
  filename: string,
  update: JailFileConfigUpdate
): Promise<void> {
  await put<undefined>(ENDPOINTS.configJailFileParsed(filename), update);
}

// ---------------------------------------------------------------------------
// Inactive jails (Stage 1)
// ---------------------------------------------------------------------------

/** Fetch all inactive jails from config files. */
export async function fetchInactiveJails(): Promise<InactiveJailListResponse> {
  return get<InactiveJailListResponse>(ENDPOINTS.configJailsInactive);
}

/**
 * Activate an inactive jail, optionally providing override values.
 *
 * @param name - The jail name.
 * @param overrides - Optional parameter overrides (bantime, findtime, etc.).
 */
export async function activateJail(
  name: string,
  overrides?: ActivateJailRequest
): Promise<JailActivationResponse> {
  return post<JailActivationResponse>(
    ENDPOINTS.configJailActivate(name),
    overrides ?? {}
  );
}

/** Deactivate an active jail. */
export async function deactivateJail(
  name: string
): Promise<JailActivationResponse> {
  return post<JailActivationResponse>(
    ENDPOINTS.configJailDeactivate(name),
    undefined
  );
}

/**
 * Delete the ``jail.d/{name}.local`` override file for an inactive jail.
 *
 * Only valid when the jail is **not** currently active.  Use this to clean up
 * leftover ``.local`` files after a jail has been fully deactivated.
 *
 * @param name - The jail name.
 */
export async function deleteJailLocalOverride(name: string): Promise<void> {
  await del<undefined>(ENDPOINTS.configJailLocalOverride(name));
}

// ---------------------------------------------------------------------------
// fail2ban log viewer (Task 2)
// ---------------------------------------------------------------------------

/**
 * Fetch the tail of the fail2ban daemon log file.
 *
 * @param lines - Number of tail lines to return (1–2000, default 200).
 * @param filter - Optional plain-text substring; only matching lines returned.
 */
export async function fetchFail2BanLog(
  lines?: number,
  filter?: string,
): Promise<Fail2BanLogResponse> {
  const params = new URLSearchParams();
  if (lines !== undefined) params.set("lines", String(lines));
  if (filter !== undefined && filter !== "") params.set("filter", filter);
  const query = params.toString() ? `?${params.toString()}` : "";
  return get<Fail2BanLogResponse>(`${ENDPOINTS.configFail2BanLog}${query}`);
}

/** Fetch fail2ban service health status with current log configuration. */
export async function fetchServiceStatus(): Promise<ServiceStatusResponse> {
  return get<ServiceStatusResponse>(ENDPOINTS.configServiceStatus);
}

// ---------------------------------------------------------------------------
// Jail config recovery (Task 3)
// ---------------------------------------------------------------------------

/**
 * Run pre-activation validation on a jail's config.
 *
 * Checks that referenced filter/action files exist, that all regex patterns
 * compile, and that log paths are accessible on the server.
 */
export async function validateJailConfig(
  name: string,
): Promise<JailValidationResult> {
  return post<JailValidationResult>(ENDPOINTS.configJailValidate(name), undefined);
}
