/**
 * ActivateJailDialog — confirmation dialog for activating an inactive jail.
 *
 * Displays the jail name and provides optional override fields for bantime,
 * findtime, maxretry, port and logpath.  Calls the activate endpoint on
 * confirmation and propagates the result via callbacks.
 *
 * Runs pre-activation validation when the dialog opens and displays any
 * warnings or blocking errors before the user confirms.
 */

import { useEffect, useState } from "react";
import {
  Button,
  Dialog,
  DialogActions,
  DialogBody,
  DialogContent,
  DialogSurface,
  DialogTitle,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  MessageBarTitle,
  Spinner,
  Text,
  tokens,
} from "@fluentui/react-components";
import type {
  ActivateJailRequest,
  JailActivationResponse,
  InactiveJail,
  JailValidationIssue,
  JailValidationResult,
} from "../../types/config";
import { ApiError } from "../../api/client";

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

export interface ActivateJailDialogProps {
  /** The inactive jail to activate, or null when the dialog is closed. */
  jail: InactiveJail | null;
  /** Whether the dialog is currently open. */
  open: boolean;
  /** Called when the dialog should be closed without taking action. */
  onClose: () => void;
  /** Called after the jail has been successfully activated. */
  onActivated: () => void;
  /** Validates the inactive jail configuration before activation. */
  onValidate: () => Promise<JailValidationResult>;
  /** Activates the jail with optional override fields. */
  onActivate: (payload: ActivateJailRequest) => Promise<JailActivationResponse>;
}

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

/**
 * Confirmation dialog for activating an inactive jail.
 *
 * All override fields are optional — leaving them blank uses the values
 * already in the config files.
 *
 * @param props - Component props.
 * @returns JSX element.
 */
export function ActivateJailDialog({
  jail,
  open,
  onClose,
  onActivated,
  onValidate,
  onActivate,
}: ActivateJailDialogProps): React.JSX.Element {
  const [bantime, setBantime] = useState("");
  const [findtime, setFindtime] = useState("");
  const [maxretry, setMaxretry] = useState("");
  const [port, setPort] = useState("");
  const [logpath, setLogpath] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [recoveryStatus, setRecoveryStatus] = useState<"recovered" | "unrecovered" | null>(null);

  // Pre-activation validation state
  const [validating, setValidating] = useState(false);
  const [validationIssues, setValidationIssues] = useState<JailValidationIssue[]>([]);
  const [validationWarnings, setValidationWarnings] = useState<string[]>([]);

  const resetForm = (): void => {
    setBantime("");
    setFindtime("");
    setMaxretry("");
    setPort("");
    setLogpath("");
    setError(null);
    setRecoveryStatus(null);
    setValidationIssues([]);
    setValidationWarnings([]);
  };

  // Run pre-validation whenever the dialog opens for a jail.
  useEffect(() => {
    if (!open || !jail) return;

    const controller = new AbortController();
    setValidating(true);
    setValidationIssues([]);
    setValidationWarnings([]);

    onValidate()
      .then((result) => {
        if (controller.signal.aborted) return;
        setValidationIssues(result.issues);
      })
      .catch(() => {
        // Validation failure is non-blocking — proceed to allow the user to
        // attempt activation and let the server decide.
      })
      .finally(() => {
        if (controller.signal.aborted) return;
        setValidating(false);
      });

    return (): void => {
      controller.abort();
    };
  }, [open, jail, onValidate]);

  const handleClose = (): void => {
    if (submitting) return;
    resetForm();
    onClose();
  };

  const handleConfirm = (): void => {
    if (!jail || submitting) return;

    const overrides: ActivateJailRequest = {};
    if (bantime.trim()) overrides.bantime = bantime.trim();
    if (findtime.trim()) overrides.findtime = findtime.trim();
    if (maxretry.trim()) {
      const n = parseInt(maxretry.trim(), 10);
      if (!isNaN(n)) overrides.maxretry = n;
    }
    if (port.trim()) overrides.port = port.trim();
    if (logpath.trim()) {
      overrides.logpath = logpath
        .split("\n")
        .map((l) => l.trim())
        .filter(Boolean);
    }

    setSubmitting(true);
    setError(null);

    onActivate(overrides)
      .then((result) => {
        if (!result.active) {
          if (result.recovered === true) {
            // Activation failed but the system rolled back automatically.
            setRecoveryStatus("recovered");
          } else if (result.recovered === false) {
            // Activation failed and rollback also failed.
            setRecoveryStatus("unrecovered");
          } else {
            // Backend rejected before writing (e.g. missing logpath or filter).
            // Show the server's message and keep the dialog open.
            setError(result.message);
          }
          return;
        }
        if (result.validation_warnings.length > 0) {
          setValidationWarnings(result.validation_warnings);
        }
        resetForm();
        onActivated();
      })
      .catch((err: unknown) => {
        const msg =
          err instanceof ApiError
            ? err.message
            : err instanceof Error
              ? err.message
              : String(err);
        setError(msg);
      })
      .finally(() => {
        setSubmitting(false);
      });
  };

  if (!jail) return <></>;

  // All validation issues block activation — logpath errors are now critical.
  const blockingIssues = validationIssues;
  const advisoryIssues: JailValidationIssue[] = [];

  return (
    <Dialog open={open} onOpenChange={(_ev, data) => { if (!data.open) handleClose(); }}>
      <DialogSurface>
        <DialogBody>
          <DialogTitle>Activate jail &ldquo;{jail.name}&rdquo;</DialogTitle>
          <DialogContent>
            <Text block style={{ marginBottom: tokens.spacingVerticalM }}>
              This will write <code>enabled = true</code> to{" "}
              <code>jail.d/{jail.name}.local</code> and reload fail2ban. The
              jail will start monitoring immediately.
            </Text>

            {/* Pre-validation results */}
            {validating && (
              <div style={{ marginBottom: tokens.spacingVerticalS, display: "flex", alignItems: "center", gap: tokens.spacingHorizontalS }}>
                <Spinner size="tiny" />
                <Text size={200}>Validating configuration…</Text>
              </div>
            )}
            {!validating && blockingIssues.length > 0 && (
              <MessageBar
                intent="error"
                style={{ marginBottom: tokens.spacingVerticalS }}
              >
                <MessageBarBody>
                  <strong>Configuration errors detected:</strong>
                  <ul style={{ margin: `${tokens.spacingVerticalXS} 0 0 0`, paddingLeft: "1.2em" }}>
                    {blockingIssues.map((issue, idx) => (
                      <li key={idx}><em>{issue.field}:</em> {issue.message}</li>
                    ))}
                  </ul>
                </MessageBarBody>
              </MessageBar>
            )}
            {!validating && advisoryIssues.length > 0 && (
              <MessageBar
                intent="warning"
                style={{ marginBottom: tokens.spacingVerticalS }}
              >
                <MessageBarBody>
                  <strong>Advisory warnings:</strong>
                  <ul style={{ margin: `${tokens.spacingVerticalXS} 0 0 0`, paddingLeft: "1.2em" }}>
                    {advisoryIssues.map((issue, idx) => (
                      <li key={idx}><em>{issue.field}:</em> {issue.message}</li>
                    ))}
                  </ul>
                </MessageBarBody>
              </MessageBar>
            )}
            {validationWarnings.length > 0 && (
              <MessageBar
                intent="warning"
                style={{ marginBottom: tokens.spacingVerticalS }}
              >
                <MessageBarBody>
                  <strong>Post-activation warnings:</strong>
                  <ul style={{ margin: `${tokens.spacingVerticalXS} 0 0 0`, paddingLeft: "1.2em" }}>
                    {validationWarnings.map((w, idx) => (
                      <li key={idx}>{w}</li>
                    ))}
                  </ul>
                </MessageBarBody>
              </MessageBar>
            )}

            <Text block weight="semibold" style={{ marginBottom: tokens.spacingVerticalS }}>
              Override values (leave blank to use config defaults)
            </Text>
            <div
              style={{
                display: "grid",
                gridTemplateColumns: "1fr 1fr",
                gap: tokens.spacingVerticalS,
              }}
            >
              <Field
                label="Ban time"
                hint={jail.bantime ? `Current: ${jail.bantime}` : undefined}
              >
                <Input
                  placeholder={jail.bantime ?? "e.g. 10m"}
                  value={bantime}
                  disabled={submitting}
                  onChange={(_e, d) => { setBantime(d.value); }}
                />
              </Field>
              <Field
                label="Find time"
                hint={jail.findtime ? `Current: ${jail.findtime}` : undefined}
              >
                <Input
                  placeholder={jail.findtime ?? "e.g. 10m"}
                  value={findtime}
                  disabled={submitting}
                  onChange={(_e, d) => { setFindtime(d.value); }}
                />
              </Field>
              <Field
                label="Max retry"
                hint={jail.maxretry != null ? `Current: ${String(jail.maxretry)}` : undefined}
              >
                <Input
                  type="number"
                  placeholder={jail.maxretry != null ? String(jail.maxretry) : "e.g. 5"}
                  value={maxretry}
                  disabled={submitting}
                  onChange={(_e, d) => { setMaxretry(d.value); }}
                />
              </Field>
              <Field
                label="Port"
                hint={jail.port ? `Current: ${jail.port}` : undefined}
              >
                <Input
                  placeholder={jail.port ?? "e.g. ssh"}
                  value={port}
                  disabled={submitting}
                  onChange={(_e, d) => { setPort(d.value); }}
                />
              </Field>
            </div>
            <Field
              label="Log path(s)"
              hint="One path per line; leave blank to use config defaults."
              style={{ marginTop: tokens.spacingVerticalS }}
            >
              <Input
                placeholder={
                  jail.logpath.length > 0 ? jail.logpath[0] : "/var/log/example.log"
                }
                value={logpath}
                disabled={submitting}
                onChange={(_e, d) => { setLogpath(d.value); }}
              />
            </Field>
            {recoveryStatus === "recovered" && (
              <MessageBar
                intent="warning"
                style={{ marginTop: tokens.spacingVerticalS }}
              >
                <MessageBarBody>
                  <MessageBarTitle>Activation Failed — Configuration Rolled Back</MessageBarTitle>
                  The configuration for jail &ldquo;{jail.name}&rdquo; has been
                  rolled back to its previous state and fail2ban is running
                  normally. Review the configuration and try activating again.
                </MessageBarBody>
              </MessageBar>
            )}
            {recoveryStatus === "unrecovered" && (
              <MessageBar
                intent="error"
                style={{ marginTop: tokens.spacingVerticalS }}
              >
                <MessageBarBody>
                  <MessageBarTitle>Activation Failed — Rollback Unsuccessful</MessageBarTitle>
                  Activation of jail &ldquo;{jail.name}&rdquo; failed and the
                  automatic rollback did not complete. The file{" "}
                  <code>jail.d/{jail.name}.local</code> may still contain{" "}
                  <code>enabled = true</code>. Check the fail2ban logs, correct
                  the file manually, and restart fail2ban.
                </MessageBarBody>
              </MessageBar>
            )}
            {error && (
              <MessageBar
                intent="error"
                style={{ marginTop: tokens.spacingVerticalS }}
              >
                <MessageBarBody>{error}</MessageBarBody>
              </MessageBar>
            )}
          </DialogContent>
          <DialogActions>
            <Button
              appearance="secondary"
              onClick={handleClose}
              disabled={submitting}
            >
              Cancel
            </Button>
            <Button
              appearance="primary"
              onClick={handleConfirm}
              disabled={submitting || validating || blockingIssues.length > 0}
              icon={submitting ? <Spinner size="tiny" /> : undefined}
            >
              {submitting ? "Activating and verifying…" : "Activate"}
            </Button>
          </DialogActions>
        </DialogBody>
      </DialogSurface>
    </Dialog>
  );
}
