/**
 * ServerTab — fail2ban server-level settings editor.
 *
 * Provides form fields for live server settings (log level, log target,
 * DB purge age, DB max matches), action buttons (flush logs, reload fail2ban,
 * restart fail2ban), world map color threshold configuration, and service
 * health + log viewer.
 */

import { useCallback, useEffect, useMemo, useReducer } from "react";
import {
  Button,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Select,
  Skeleton,
  SkeletonItem,
  Text,
  tokens,
} from "@fluentui/react-components";
import {
  DocumentArrowDown24Regular,
  ArrowSync24Regular,
} from "@fluentui/react-icons";
import { ApiError } from "../../api/client";
import type { ServerSettingsUpdate, MapColorThresholdsUpdate } from "../../types/config";
import { useServerSettings } from "../../hooks/useServerSettings";
import { useAutoSave } from "../../hooks/useAutoSave";
import { useMapColorThresholds } from "../../hooks/useMapColorThresholds";
import { AutoSaveIndicator } from "./AutoSaveIndicator";
import { ServerHealthSection } from "./ServerHealthSection";
import { useConfigStyles } from "./configStyles";

/** Available fail2ban log levels in descending severity order. */
const LOG_LEVELS = ["CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"];

interface ServerTabMessage {
  text: string;
  ok: boolean;
}

interface ServerTabState {
  logLevel: string;
  logTarget: string;
  dbPurgeAge: string;
  dbMaxMatches: string;
  flushing: boolean;
  msg: ServerTabMessage | null;
  isReloading: boolean;
  isRestarting: boolean;
  mapThresholdHigh: string;
  mapThresholdMedium: string;
  mapThresholdLow: string;
  mapValidPayload: MapColorThresholdsUpdate;
}

type ServerTabAction =
  | { type: "setLogLevel"; value: string }
  | { type: "setLogTarget"; value: string }
  | { type: "setDbPurgeAge"; value: string }
  | { type: "setDbMaxMatches"; value: string }
  | { type: "setFlushing"; value: boolean }
  | { type: "setMsg"; value: ServerTabMessage | null }
  | { type: "setIsReloading"; value: boolean }
  | { type: "setIsRestarting"; value: boolean }
  | {
      type: "setMapThresholds";
      high: string;
      medium: string;
      low: string;
    }
  | { type: "setMapValidPayload"; payload: MapColorThresholdsUpdate };

export const initialServerTabState: ServerTabState = {
  logLevel: "",
  logTarget: "",
  dbPurgeAge: "",
  dbMaxMatches: "",
  flushing: false,
  msg: null,
  isReloading: false,
  isRestarting: false,
  mapThresholdHigh: "",
  mapThresholdMedium: "",
  mapThresholdLow: "",
  mapValidPayload: {
    threshold_high: 0,
    threshold_medium: 0,
    threshold_low: 0,
  },
};

export function serverTabReducer(
  state: ServerTabState,
  action: ServerTabAction,
): ServerTabState {
  switch (action.type) {
    case "setLogLevel":
      return { ...state, logLevel: action.value };
    case "setLogTarget":
      return { ...state, logTarget: action.value };
    case "setDbPurgeAge":
      return { ...state, dbPurgeAge: action.value };
    case "setDbMaxMatches":
      return { ...state, dbMaxMatches: action.value };
    case "setFlushing":
      return { ...state, flushing: action.value };
    case "setMsg":
      return { ...state, msg: action.value };
    case "setIsReloading":
      return { ...state, isReloading: action.value };
    case "setIsRestarting":
      return { ...state, isRestarting: action.value };
    case "setMapThresholds":
      return {
        ...state,
        mapThresholdHigh: action.high,
        mapThresholdMedium: action.medium,
        mapThresholdLow: action.low,
      };
    case "setMapValidPayload":
      return { ...state, mapValidPayload: action.payload };
    default:
      return state;
  }
}

/**
 * Tab component for editing live fail2ban server settings.
 *
 * @returns JSX element.
 */
export function ServerTab(): React.JSX.Element {
  const styles = useConfigStyles();
  const { settings, loading, error, updateSettings, flush, reload, restart } =
    useServerSettings();
  const [state, dispatch] = useReducer(serverTabReducer, initialServerTabState);

  const {
    logLevel,
    logTarget,
    dbPurgeAge,
    dbMaxMatches,
    flushing,
    msg,
    isReloading,
    isRestarting,
    mapThresholdHigh,
    mapThresholdMedium,
    mapThresholdLow,
    mapValidPayload,
  } = state;

  const {
    thresholds: mapThresholds,
    error: mapThresholdsError,
    refresh: refreshMapThresholds,
    updateThresholds: updateMapThresholds,
  } = useMapColorThresholds();

  const effectiveLogLevel = logLevel || settings?.log_level || "";
  const effectiveLogTarget = logTarget || settings?.log_target || "";
  const effectiveDbPurgeAge =
    dbPurgeAge || (settings ? String(settings.db_purge_age) : "");
  const effectiveDbMaxMatches =
    dbMaxMatches || (settings ? String(settings.db_max_matches) : "");

  const updatePayload = useMemo<ServerSettingsUpdate>(() => {
    const update: ServerSettingsUpdate = {};
    if (effectiveLogLevel) update.log_level = effectiveLogLevel;
    if (effectiveLogTarget) update.log_target = effectiveLogTarget;
    if (effectiveDbPurgeAge) update.db_purge_age = Number(effectiveDbPurgeAge);
    if (effectiveDbMaxMatches)
      update.db_max_matches = Number(effectiveDbMaxMatches);
    return update;
  }, [effectiveLogLevel, effectiveLogTarget, effectiveDbPurgeAge, effectiveDbMaxMatches]);

  const { status: saveStatus, errorText: saveErrorText, retry: retrySave } =
    useAutoSave(updatePayload, updateSettings);

  const handleFlush = useCallback(async () => {
    dispatch({ type: "setFlushing", value: true });
    dispatch({ type: "setMsg", value: null });
    try {
      const result = await flush();
      dispatch({ type: "setMsg", value: { text: `Logs flushed: ${result}`, ok: true } });
    } catch (err: unknown) {
      dispatch({
        type: "setMsg",
        value: {
          text: err instanceof ApiError ? err.message : "Flush failed.",
          ok: false,
        },
      });
    } finally {
      dispatch({ type: "setFlushing", value: false });
    }
  }, [flush]);

  const handleReload = useCallback(async (): Promise<void> => {
    dispatch({ type: "setIsReloading", value: true });
    dispatch({ type: "setMsg", value: null });
    try {
      await reload();
      dispatch({
        type: "setMsg",
        value: { text: "fail2ban reloaded successfully", ok: true },
      });
    } catch (err: unknown) {
      dispatch({
        type: "setMsg",
        value: {
          text: err instanceof ApiError ? err.message : "Reload failed.",
          ok: false,
        },
      });
    } finally {
      dispatch({ type: "setIsReloading", value: false });
    }
  }, [reload]);

  const handleRestart = useCallback(async (): Promise<void> => {
    dispatch({ type: "setIsRestarting", value: true });
    dispatch({ type: "setMsg", value: null });
    try {
      await restart();
      dispatch({
        type: "setMsg",
        value: { text: "fail2ban restart initiated", ok: true },
      });
    } catch (err: unknown) {
      dispatch({
        type: "setMsg",
        value: {
          text: err instanceof ApiError ? err.message : "Restart failed.",
          ok: false,
        },
      });
    } finally {
      dispatch({ type: "setIsRestarting", value: false });
    }
  }, [restart]);

  useEffect(() => {
    if (!mapThresholds) return;

    dispatch({
      type: "setMapThresholds",
      high: String(mapThresholds.threshold_high),
      medium: String(mapThresholds.threshold_medium),
      low: String(mapThresholds.threshold_low),
    });
  }, [mapThresholds]);

  const mapHigh = Number(mapThresholdHigh);
  const mapMedium = Number(mapThresholdMedium);
  const mapLow = Number(mapThresholdLow);

  const mapValidationError = useMemo<string | null>(() => {
    if (!mapThresholds) return null;
    if (isNaN(mapHigh) || isNaN(mapMedium) || isNaN(mapLow))
      return "All thresholds must be valid numbers.";
    if (mapHigh <= 0 || mapMedium <= 0 || mapLow <= 0)
      return "All thresholds must be positive integers.";
    if (!(mapHigh > mapMedium && mapMedium > mapLow))
      return "Thresholds must satisfy: high > medium > low.";
    return null;
  }, [mapHigh, mapMedium, mapLow, mapThresholds]);

  useEffect(() => {
    if (mapValidationError !== null || !mapThresholds) return;
    dispatch({
      type: "setMapValidPayload",
      payload: {
        threshold_high: mapHigh,
        threshold_medium: mapMedium,
        threshold_low: mapLow,
      },
    });
  }, [mapHigh, mapMedium, mapLow, mapValidationError, mapThresholds]);

  const saveMapThresholds = useCallback(
    async (payload: MapColorThresholdsUpdate): Promise<void> => {
      await updateMapThresholds(payload);
      await refreshMapThresholds();
    },
    [refreshMapThresholds, updateMapThresholds],
  );

  const { status: mapSaveStatus, errorText: mapSaveErrorText, retry: retryMapSave } =
    useAutoSave(mapValidPayload, saveMapThresholds);

  if (loading) {
    return (
      <Skeleton aria-label="Loading server settings…">
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: 8 }}>
          <SkeletonItem size={32} />
          <SkeletonItem size={32} />
        </div>
        <SkeletonItem size={32} style={{ marginBottom: 8 }} />
        <SkeletonItem size={32} />
      </Skeleton>
    );
  }
  if (error)
    return (
      <MessageBar intent="error">
        <MessageBarBody>{error}</MessageBarBody>
      </MessageBar>
    );

  return (
    <div>
      {/* Service Health & Log Viewer section — shown first so users can
          immediately see whether fail2ban is reachable before editing settings. */}
      <ServerHealthSection />

      <div className={styles.sectionCard}>
        <div style={{ marginBottom: tokens.spacingVerticalS }}>
          <AutoSaveIndicator
            status={saveStatus}
            errorText={saveErrorText}
            onRetry={retrySave}
          />
        </div>
        <div className={styles.fieldRow}>
          <Field label="Log Level">
            <Select
              value={effectiveLogLevel}
              onChange={(_e, d) => {
                dispatch({ type: "setLogLevel", value: d.value });
              }}
            >
              {LOG_LEVELS.map((l) => (
                <option key={l} value={l}>
                  {l}
                </option>
              ))}
            </Select>
          </Field>
          <Field label="Log Target">
            <Input
              value={effectiveLogTarget}
              placeholder="STDOUT / /var/log/fail2ban.log"
              onChange={(_e, d) => {
                dispatch({ type: "setLogTarget", value: d.value });
              }}
            />
          </Field>
        </div>
        <div className={styles.fieldRow}>
          <Field label="DB Path">
            <Input
              readOnly
              value={settings?.db_path ?? ""}
              className={styles.codeFont}
            />
          </Field>
          <Field label="Syslog Socket">
            <Input
              readOnly
              value={settings?.syslog_socket ?? "(not configured)"}
              className={styles.codeFont}
            />
          </Field>
        </div>
        <div className={styles.fieldRow}>
          <Field
            label="DB Purge Age (s)"
            hint="Ban records older than this are removed from the fail2ban database."
          >
            <Input
              type="number"
              value={effectiveDbPurgeAge}
              onChange={(_e, d) => {
                dispatch({ type: "setDbPurgeAge", value: d.value });
              }}
            />
          </Field>
          <Field
            label="DB Max Matches"
            hint="Maximum number of log-line matches stored per ban record."
          >
            <Input
              type="number"
              value={effectiveDbMaxMatches}
              onChange={(_e, d) => {
                dispatch({ type: "setDbMaxMatches", value: d.value });
              }}
            />
          </Field>
        </div>
        <div className={styles.buttonRow}>
          <Button
            appearance="secondary"
            icon={<DocumentArrowDown24Regular />}
            disabled={flushing}
            onClick={() => void handleFlush()}
          >
            {flushing ? "Flushing…" : "Flush Logs"}
          </Button>
          <Button
            appearance="secondary"
            icon={<ArrowSync24Regular />}
            disabled={isReloading}
            onClick={() => void handleReload()}
          >
            {isReloading ? "Reloading…" : "Reload fail2ban"}
          </Button>
          <Button
            appearance="secondary"
            icon={<ArrowSync24Regular />}
            disabled={isRestarting}
            onClick={() => void handleRestart()}
          >
            {isRestarting ? "Restarting…" : "Restart fail2ban"}
          </Button>
        </div>
        {msg && (
          <MessageBar intent={msg.ok ? "success" : "error"}>
            <MessageBarBody>{msg.text}</MessageBarBody>
          </MessageBar>
        )}
      </div>

      {/* Map Color Thresholds section */}
      {mapThresholdsError ? (
        <div className={styles.sectionCard}>
          <MessageBar intent="error">
            <MessageBarBody>{mapThresholdsError}</MessageBarBody>
          </MessageBar>
        </div>
      ) : mapThresholds ? (
        <div className={styles.sectionCard}>
          <Text as="h3" size={500} weight="semibold" block>
            Map Color Thresholds
          </Text>
          <Text
            as="p"
            size={300}
            className={styles.infoText}
            block
            style={{ marginBottom: tokens.spacingVerticalM }}
          >
            Configure the ban count thresholds that determine country fill colors on
            the World Map. Countries with zero bans remain transparent. Colors
            smoothly interpolate between thresholds.
          </Text>

          <div style={{ marginBottom: tokens.spacingVerticalS }}>
            <AutoSaveIndicator
              status={mapValidationError ? "idle" : mapSaveStatus}
              errorText={mapSaveErrorText}
              onRetry={retryMapSave}
            />
          </div>

          {mapValidationError && (
            <MessageBar intent="error" style={{ marginBottom: tokens.spacingVerticalS }}>
              <MessageBarBody>{mapValidationError}</MessageBarBody>
            </MessageBar>
          )}

          <div className={styles.fieldRowThree}>
            <Field label="Low Threshold (Green)" required>
              <Input
                type="number"
                value={mapThresholdLow}
                onChange={(_, d) => {
                  dispatch({ type: "setMapThresholds", high: mapThresholdHigh, medium: mapThresholdMedium, low: d.value });
                }}
                min={1}
              />
            </Field>
            <Field label="Medium Threshold (Yellow)" required>
              <Input
                type="number"
                value={mapThresholdMedium}
                onChange={(_, d) => {
                  dispatch({ type: "setMapThresholds", high: mapThresholdHigh, medium: d.value, low: mapThresholdLow });
                }}
                min={1}
              />
            </Field>
            <Field label="High Threshold (Red)" required>
              <Input
                type="number"
                value={mapThresholdHigh}
                onChange={(_, d) => {
                  dispatch({ type: "setMapThresholds", high: d.value, medium: mapThresholdMedium, low: mapThresholdLow });
                }}
                min={1}
              />
            </Field>
          </div>

          <Text
            as="p"
            size={200}
            className={styles.infoText}
            style={{ marginTop: tokens.spacingVerticalS }}
          >
            • 1 to {mapThresholdLow}: Light green → Full green
            <br />• {mapThresholdLow} to {mapThresholdMedium}: Green → Yellow
            <br />• {mapThresholdMedium} to {mapThresholdHigh}: Yellow → Red
            <br />• {mapThresholdHigh}+: Solid red
          </Text>
        </div>
      ) : null}

    </div>
  );
}
