/**
 * AssignFilterDialog — dialog for assigning a filter to a fail2ban jail.
 *
 * Fetches the list of active jails for the dropdown, then calls
 * ``POST /api/config/jails/{jail_name}/filter`` on confirmation.
 */

import { useCallback, useEffect, useState } from "react";
import {
  Button,
  Checkbox,
  Dialog,
  DialogActions,
  DialogBody,
  DialogContent,
  DialogSurface,
  DialogTitle,
  Field,
  MessageBar,
  MessageBarBody,
  Select,
  Spinner,
  Text,
  tokens,
} from "@fluentui/react-components";
import { useJails } from "../../hooks/useJails";
import type { AssignFilterRequest } from "../../types/config";
import { ApiError } from "../../api/client";

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface AssignFilterDialogProps {
  /**
   * The filter base name to assign (e.g. ``"sshd"``).  Set to ``null`` when
   * the dialog is closed.
   */
  filterName: string | null;
  /** Whether the dialog is currently open. */
  open: boolean;
  /** Called when the dialog should close without taking action. */
  onClose: () => void;
  /** Called after the filter has been successfully assigned. */
  onAssigned: () => void;
  /** Assigns the filter to a running jail. */
  onAssign: (jailName: string, payload: AssignFilterRequest, reload: boolean) => Promise<void>;
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

/**
 * Confirmation dialog for assigning a filter to a jail.
 *
 * Fetches running jails from the API for the jail selector.  The user can
 * optionally request a fail2ban reload immediately after the assignment is
 * written.
 *
 * @param props - Component props.
 * @returns JSX element.
 */
export function AssignFilterDialog({
  filterName,
  open,
  onClose,
  onAssigned,
  onAssign,
}: AssignFilterDialogProps): React.JSX.Element {
  const { jails, loading: jailsLoading, error: jailsError } = useJails();
  const [selectedJail, setSelectedJail] = useState("");
  const [reload, setReload] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const activeJails = jails.filter((j) => j.enabled);

  useEffect(() => {
    if (!open) return;
    setError(null);
    setSelectedJail("");
    setReload(false);
  }, [open]);

  useEffect(() => {
    if (jailsError && open) {
      setError(jailsError);
    }
  }, [jailsError, open]);

  const handleClose = useCallback((): void => {
    if (submitting) return;
    setError(null);
    onClose();
  }, [submitting, onClose]);

  const handleConfirm = useCallback((): void => {
    if (!filterName || !selectedJail || submitting) return;

    const req: AssignFilterRequest = { filter_name: filterName };
    setSubmitting(true);
    setError(null);

    onAssign(selectedJail, req, reload)
      .then(() => {
        onAssigned();
      })
      .catch((err: unknown) => {
        setError(
          err instanceof ApiError ? err.message : "Failed to assign filter.",
        );
      })
      .finally(() => {
        setSubmitting(false);
      });
  }, [filterName, selectedJail, reload, submitting, onAssigned, onAssign]);

  const canConfirm = selectedJail !== "" && !submitting && !jailsLoading;

  return (
    <Dialog open={open} onOpenChange={(_e, data) => { if (!data.open) handleClose(); }}>
      <DialogSurface>
        <DialogBody>
          <DialogTitle>Assign Filter to Jail</DialogTitle>
          <DialogContent>
            {filterName !== null && (
              <Text
                as="p"
                size={300}
                style={{ marginBottom: tokens.spacingVerticalM }}
              >
                Assign filter{" "}
                <Text weight="semibold" style={{ fontFamily: "monospace" }}>
                  {filterName}
                </Text>{" "}
                to a jail.  This writes{" "}
                <Text style={{ fontFamily: "monospace" }}>filter = {filterName}</Text>{" "}
                into the jail&apos;s <code>.local</code> override file.
              </Text>
            )}

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

            <Field
              label="Target jail"
              required
              hint="Only currently enabled jails are listed."
            >
              {jailsLoading ? (
                <Spinner size="extra-small" label="Loading jails…" />
              ) : (
                <Select
                  value={selectedJail}
                  onChange={(_e, d) => { setSelectedJail(d.value); }}
                  aria-label="Target jail"
                >
                  <option value="" disabled>
                    — select a jail —
                  </option>
                  {activeJails.map((j) => (
                    <option key={j.name} value={j.name}>
                      {j.name}
                    </option>
                  ))}
                </Select>
              )}
            </Field>

            <Checkbox
              label="Reload fail2ban after assigning"
              checked={reload}
              onChange={(_e, d) => { setReload(Boolean(d.checked)); }}
              style={{ marginTop: tokens.spacingVerticalS }}
            />
          </DialogContent>
          <DialogActions>
            <Button
              appearance="secondary"
              onClick={handleClose}
              disabled={submitting}
            >
              Cancel
            </Button>
            <Button
              appearance="primary"
              onClick={handleConfirm}
              disabled={!canConfirm}
              icon={submitting ? <Spinner size="extra-small" /> : undefined}
            >
              {submitting ? "Assigning…" : "Assign"}
            </Button>
          </DialogActions>
        </DialogBody>
      </DialogSurface>
    </Dialog>
  );
}
