/**
 * JailFilesTab — manage jail.d config files with a structured form editor.
 *
 * Displays all jail.d config files in a collapsible accordion.  Each file
 * panel renders a ``JailFileForm`` with per-section auto-save editing.  The
 * file-level enable/disable toggle remains in the accordion header.
 *
 * A "Create new jail file" section at the bottom allows adding new files.
 */

import { useCallback, useEffect, useState } from "react";
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  Button,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Skeleton,
  SkeletonItem,
  Switch,
  Text,
  tokens,
} from "@fluentui/react-components";
import { Add24Regular, ArrowClockwise24Regular, DocumentAdd24Regular } from "@fluentui/react-icons";
import { ApiError } from "../../api/client";
import {
  createJailConfigFile,
  fetchJailConfigFiles,
  setJailConfigFileEnabled,
} from "../../api/config";
import type { JailConfigFile } from "../../types/config";
import { JailFileForm } from "./JailFileForm";
import { useConfigStyles } from "./configStyles";

/**
 * Tab component for managing jail.d configuration files with structured forms.
 *
 * @returns JSX element.
 */
export function JailFilesTab(): React.JSX.Element {
  const styles = useConfigStyles();
  const [files, setFiles] = useState<JailConfigFile[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [toggling, setToggling] = useState<string | null>(null);
  const [msg, setMsg] = useState<{ text: string; ok: boolean } | null>(null);

  // Create new file form state
  const [newFilename, setNewFilename] = useState("");
  const [creating, setCreating] = useState(false);
  const [createError, setCreateError] = useState<string | null>(null);

  const loadFiles = useCallback(async (): Promise<void> => {
    setLoading(true);
    setError(null);
    try {
      const resp = await fetchJailConfigFiles();
      setFiles(resp.files);
    } catch (err: unknown) {
      setError(
        err instanceof ApiError ? err.message : "Failed to load jail config files.",
      );
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    void loadFiles();
  }, [loadFiles]);

  const handleToggleEnabled = useCallback(
    async (filename: string, enabled: boolean): Promise<void> => {
      setToggling(filename);
      setMsg(null);
      try {
        await setJailConfigFileEnabled(filename, { enabled });
        setFiles((prev) =>
          prev.map((f) => (f.filename === filename ? { ...f, enabled } : f)),
        );
        setMsg({ text: `${filename} ${enabled ? "enabled" : "disabled"}.`, ok: true });
      } catch (err: unknown) {
        setMsg({
          text: err instanceof ApiError ? err.message : "Toggle failed.",
          ok: false,
        });
      } finally {
        setToggling(null);
      }
    },
    [],
  );

  const handleCreate = useCallback(async (): Promise<void> => {
    const name = newFilename.trim();
    if (!name) return;
    setCreating(true);
    setCreateError(null);
    try {
      await createJailConfigFile({ name, content: `# ${name}\n` });
      setNewFilename("");
      await loadFiles();
    } catch (err: unknown) {
      setCreateError(err instanceof ApiError ? err.message : "Failed to create file.");
    } finally {
      setCreating(false);
    }
  }, [newFilename, loadFiles]);

  if (loading) {
    return (
      <Skeleton aria-label="Loading jail config files…">
        {[0, 1, 2].map((i) => (
          <SkeletonItem key={i} size={40} style={{ marginBottom: 4 }} />
        ))}
      </Skeleton>
    );
  }
  if (error) {
    return (
      <MessageBar intent="error">
        <MessageBarBody>{error}</MessageBarBody>
      </MessageBar>
    );
  }

  return (
    <div>
      <Text
        as="p"
        size={300}
        className={styles.infoText}
        block
        style={{ marginBottom: tokens.spacingVerticalM }}
      >
        Files in <code>jail.d/</code>. Toggle the switch to enable or disable a
        jail config file. Changes take effect on the next fail2ban reload.
      </Text>

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

      <div className={styles.buttonRow}>
        <Button
          appearance="secondary"
          icon={<ArrowClockwise24Regular />}
          onClick={() => { void loadFiles(); }}
        >
          Refresh
        </Button>
      </div>

      {files.length === 0 && (
        <div className={styles.emptyState}>
          <DocumentAdd24Regular
            style={{ fontSize: 48, color: tokens.colorNeutralForeground3 }}
            aria-hidden
          />
          <Text size={400} style={{ color: tokens.colorNeutralForeground3 }}>
            No files found in jail.d/.
          </Text>
          <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
            Use the form below to create your first jail config file.
          </Text>
        </div>
      )}

      <Accordion
        multiple
        collapsible
        style={{ marginTop: tokens.spacingVerticalM }}
      >
        {files.map((file) => (
          <AccordionItem key={file.filename} value={file.filename} className={styles.accordionItem}>
            <AccordionHeader>
              <span
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: tokens.spacingHorizontalM,
                }}
              >
                <span className={styles.codeFont}>{file.filename}</span>
                <Switch
                  checked={file.enabled}
                  disabled={toggling === file.filename}
                  label={file.enabled ? "Enabled" : "Disabled"}
                  onChange={(_e, d) => {
                    void handleToggleEnabled(file.filename, d.checked);
                  }}
                  onClick={(e) => { e.stopPropagation(); }}
                />
              </span>
            </AccordionHeader>
            <AccordionPanel>
              <JailFileForm filename={file.filename} />
            </AccordionPanel>
          </AccordionItem>
        ))}
      </Accordion>

      {/* Create new jail file */}
      <div
        style={{
          marginTop: tokens.spacingVerticalXL,
          padding: tokens.spacingVerticalM,
          borderTop: `1px solid ${tokens.colorNeutralStroke2}`,
        }}
      >
        <Text size={400} weight="semibold" block style={{ marginBottom: tokens.spacingVerticalS }}>
          Create New Jail File
        </Text>
        {createError !== null && (
          <MessageBar intent="error" style={{ marginBottom: tokens.spacingVerticalS }}>
            <MessageBarBody>{createError}</MessageBarBody>
          </MessageBar>
        )}
        <div style={{ display: "flex", gap: tokens.spacingHorizontalM, alignItems: "flex-end" }}>
          <Field label="Filename" style={{ flex: 1 }}>
            <Input
              value={newFilename}
              placeholder="e.g. myapp.conf"
              onChange={(_e, d) => { setNewFilename(d.value); }}
              onKeyDown={(e) => {
                if (e.key === "Enter") { void handleCreate(); }
              }}
            />
          </Field>
          <Button
            appearance="primary"
            icon={<Add24Regular />}
            disabled={creating || !newFilename.trim()}
            onClick={() => { void handleCreate(); }}
          >
            {creating ? "Creating…" : "Create"}
          </Button>
        </div>
      </div>
    </div>
  );
}

