/**
 * JailFileForm — structured form editor for a single ``jail.d/*.conf`` file.
 *
 * Renders each jail section in the file as an accordion panel with fields for
 * all common jail settings plus log paths, action references, and extra keys.
 *
 * All fields auto-save through ``useAutoSave``.
 */

import { useCallback, useEffect, useMemo, useState } from "react";
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  Badge,
  Button,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Select,
  Skeleton,
  SkeletonItem,
  Switch,
  Text,
  tokens,
} from "@fluentui/react-components";
import { Add24Regular, Delete24Regular, DocumentAdd24Regular } from "@fluentui/react-icons";
import type { JailFileConfig, JailFileConfigUpdate, JailSectionConfig } from "../../types/config";
import { useJailFileConfig } from "../../hooks/useJailFileConfig";
import { useAutoSave } from "../../hooks/useAutoSave";
import { AutoSaveIndicator } from "./AutoSaveIndicator";
import { useConfigStyles } from "./configStyles";

// ---------------------------------------------------------------------------
// StringListEditor — add / remove lines in a list field
// ---------------------------------------------------------------------------

interface StringListEditorProps {
  items: string[];
  onChange: (next: string[]) => void;
  placeholder?: string;
  addLabel?: string;
}

function StringListEditor({
  items,
  onChange,
  placeholder,
  addLabel = "Add entry",
}: StringListEditorProps): React.JSX.Element {
  const handleChange = (index: number, value: string): void => {
    const next = [...items];
    next[index] = value;
    onChange(next);
  };

  const handleDelete = (index: number): void => {
    onChange(items.filter((_, i) => i !== index));
  };

  const handleAdd = (): void => {
    onChange([...items, ""]);
  };

  return (
    <div>
      {items.map((item, index) => (
        <div key={index} style={{ display: "flex", gap: 4, marginBottom: 4, alignItems: "center" }}>
          <Input
            value={item}
            size="small"
            style={{ flex: 1, fontFamily: "monospace" }}
            placeholder={placeholder}
            aria-label={`Entry ${String(index + 1)}${item ? `: ${item}` : ""}`}
            onChange={(_e, d) => { handleChange(index, d.value); }}
          />
          <Button
            icon={<Delete24Regular />}
            size="small"
            appearance="subtle"
            onClick={() => { handleDelete(index); }}
            aria-label="Remove entry"
          />
        </div>
      ))}
      <Button
        icon={<Add24Regular />}
        size="small"
        appearance="outline"
        onClick={handleAdd}
        style={{ marginTop: 4 }}
      >
        {addLabel}
      </Button>
    </div>
  );
}

// ---------------------------------------------------------------------------
// KVEditor — key-value pair list (for extra settings)
// ---------------------------------------------------------------------------

interface KVEditorProps {
  entries: Record<string, string>;
  onChange: (next: Record<string, string>) => void;
}

function KVEditor({ entries, onChange }: KVEditorProps): React.JSX.Element {
  const styles = useConfigStyles();
  const rows = Object.entries(entries);

  const handleKeyChange = (oldKey: string, newKey: string): void => {
    const next: Record<string, string> = {};
    for (const [k, v] of Object.entries(entries)) {
      next[k === oldKey ? newKey : k] = v;
    }
    onChange(next);
  };

  const handleValueChange = (key: string, value: string): void => {
    onChange({ ...entries, [key]: value });
  };

  const handleDelete = (key: string): void => {
    const { [key]: _removed, ...rest } = entries;
    onChange(rest);
  };

  const handleAdd = (): void => {
    let newKey = "key";
    let n = 1;
    while (newKey in entries) {
      newKey = `key${String(n)}`;
      n++;
    }
    onChange({ ...entries, [newKey]: "" });
  };

  return (
    <div>
      {rows.map(([key, value]) => (
        <div key={key} className={styles.fieldRow}>
          <Input
            value={key}
            size="small"
            style={{ width: 140, fontFamily: "monospace" }}
            aria-label={`Setting name: ${key}`}
            onChange={(_e, d) => { handleKeyChange(key, d.value); }}
          />
          <Input
            value={value}
            size="small"
            style={{ flex: 1, fontFamily: "monospace" }}
            aria-label={`Value for ${key}`}
            onChange={(_e, d) => { handleValueChange(key, d.value); }}
          />
          <Button
            icon={<Delete24Regular />}
            size="small"
            appearance="subtle"
            onClick={() => { handleDelete(key); }}
            aria-label={`Delete key ${key}`}
          />
        </div>
      ))}
      <Button
        icon={<Add24Regular />}
        size="small"
        appearance="outline"
        onClick={handleAdd}
        style={{ marginTop: 4 }}
      >
        Add setting
      </Button>
    </div>
  );
}

// ---------------------------------------------------------------------------
// JailSectionPanel — form fields for one [jailname] section
// ---------------------------------------------------------------------------

const BACKENDS = ["", "auto", "polling", "gamin", "pyinotify", "systemd"] as const;

interface JailSectionPanelProps {
  jailName: string;
  section: JailSectionConfig;
  onChange: (next: JailSectionConfig) => void;
}

function JailSectionPanel({
  jailName,
  section,
  onChange,
}: JailSectionPanelProps): React.JSX.Element {
  const styles = useConfigStyles();
  const update = useCallback(
    (patch: Partial<JailSectionConfig>): void => {
      onChange({ ...section, ...patch });
    },
    [onChange, section]
  );

  return (
    <div>
      {/* Core fields grid */}
      <div className={styles.sectionCard}>
        <div className={styles.fieldRow}>
          <Field label="Enabled">
            <Switch
              checked={section.enabled ?? false}
              onChange={(_e, d) => { update({ enabled: d.checked }); }}
              aria-label={`Enable jail ${jailName}`}
            />
          </Field>
          <Field label="Port">
            <Input
              value={section.port ?? ""}
              size="small"
              placeholder="e.g. ssh or 22"
              onChange={(_e, d) => { update({ port: d.value || null }); }}
            />
          </Field>
          <Field label="Filter">
            <Input
              value={section.filter ?? ""}
              size="small"
              placeholder="e.g. sshd"
              className={styles.codeInput}
              onChange={(_e, d) => { update({ filter: d.value || null }); }}
            />
          </Field>
          <Field label="Backend">
            <Select
              size="small"
              value={section.backend ?? ""}
              onChange={(_e, d) => { update({ backend: d.value || null }); }}
            >
              {BACKENDS.map((b) => (
                <option key={b} value={b}>{b === "" ? "(default)" : b}</option>
              ))}
            </Select>
          </Field>
          <Field label="Max Retry">
            <Input
              value={section.maxretry !== null ? String(section.maxretry) : ""}
              size="small"
              type="number"
              placeholder="e.g. 5"
              onChange={(_e, d) => {
                const n = parseInt(d.value, 10);
                update({ maxretry: d.value === "" ? null : isNaN(n) ? null : n });
              }}
            />
          </Field>
          <Field label="Find Time (s)">
            <Input
              value={section.findtime !== null ? String(section.findtime) : ""}
              size="small"
              type="number"
              placeholder="e.g. 600"
              onChange={(_e, d) => {
                const n = parseInt(d.value, 10);
                update({ findtime: d.value === "" ? null : isNaN(n) ? null : n });
              }}
            />
          </Field>
          <Field label="Ban Time (s)">
            <Input
              value={section.bantime !== null ? String(section.bantime) : ""}
              size="small"
              type="number"
              placeholder="e.g. 3600"
              onChange={(_e, d) => {
                const n = parseInt(d.value, 10);
                update({ bantime: d.value === "" ? null : isNaN(n) ? null : n });
              }}
            />
          </Field>
        </div>
      </div>

      {/* Log paths */}
      <Accordion multiple collapsible defaultOpenItems={["logpath"]}>
        <AccordionItem value="logpath" className={styles.accordionItem}>
          <AccordionHeader>Log Paths ({section.logpath.length})</AccordionHeader>
          <AccordionPanel>
            <div className={styles.sectionCard}>
              <StringListEditor
                items={section.logpath}
                onChange={(next) => { update({ logpath: next }); }}
                placeholder="e.g. /var/log/auth.log"
                addLabel="Add log path"
              />
            </div>
          </AccordionPanel>
        </AccordionItem>

        {/* Actions */}
        <AccordionItem value="actions" className={styles.accordionItem}>
          <AccordionHeader>Actions ({section.action.length})</AccordionHeader>
          <AccordionPanel>
            <div className={styles.sectionCard}>
              <StringListEditor
                items={section.action}
                onChange={(next) => { update({ action: next }); }}
                placeholder="e.g. iptables-multiport[name=SSH, port=ssh]"
                addLabel="Add action"
              />
            </div>
          </AccordionPanel>
        </AccordionItem>

        {/* Extra settings */}
        {(Object.keys(section.extra).length > 0) && (
          <AccordionItem value="extra" className={styles.accordionItem}>
            <AccordionHeader>Extra Settings ({Object.keys(section.extra).length})</AccordionHeader>
            <AccordionPanel>
              <div className={styles.sectionCard}>
                <KVEditor
                  entries={section.extra}
                  onChange={(next) => { update({ extra: next }); }}
                />
              </div>
            </AccordionPanel>
          </AccordionItem>
        )}
      </Accordion>
    </div>
  );
}

// ---------------------------------------------------------------------------
// JailFileFormInner — rendered once config is loaded
// ---------------------------------------------------------------------------

interface JailFileFormInnerProps {
  config: JailFileConfig;
  onSave: (update: JailFileConfigUpdate) => Promise<void>;
}

function JailFileFormInner({
  config,
  onSave,
}: JailFileFormInnerProps): React.JSX.Element {
  const [jails, setJails] = useState<Record<string, JailSectionConfig>>(config.jails);

  // Reset when a freshly-loaded config arrives.
  useEffect(() => {
    setJails(config.jails);
  }, [config]);

  const autoSavePayload = useMemo<JailFileConfigUpdate>(
    () => ({ jails }),
    [jails]
  );

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

  const handleSectionChange = useCallback(
    (jailName: string, next: JailSectionConfig): void => {
      setJails((prev) => ({ ...prev, [jailName]: next }));
    },
    []
  );

  const styles = useConfigStyles();
  const jailNames = Object.keys(jails);

  return (
    <div>
      {/* Header row */}
      <div className={styles.buttonRow} style={{ marginBottom: 8 }}>
        <Text size={500} weight="semibold">{config.filename}</Text>
        <AutoSaveIndicator
          status={saveStatus}
          errorText={saveErrorText}
          onRetry={retrySave}
        />
      </div>

      {jailNames.length === 0 ? (
        <div className={styles.emptyState}>
          <DocumentAdd24Regular
            style={{ fontSize: 48, color: tokens.colorNeutralForeground3 }}
            aria-hidden
          />
          <Text size={400} style={{ color: tokens.colorNeutralForeground3 }}>
            No jail sections found in this file.
          </Text>
          <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
            Add a <code>[jailname]</code> section to the file to define jails.
          </Text>
        </div>
      ) : (
        <Accordion multiple collapsible defaultOpenItems={jailNames}>
          {jailNames.map((jailName) => {
            const section = jails[jailName];
            if (section === undefined) return null;
            return (
              <AccordionItem key={jailName} value={jailName} className={styles.accordionItem}>
                <AccordionHeader>
                  <Badge appearance="filled" color="informative" style={{ marginRight: 6 }}>
                    {jailName}
                  </Badge>
                  {section.enabled === true
                    ? "(enabled)"
                    : section.enabled === false
                      ? "(disabled)"
                      : ""}
                </AccordionHeader>
                <AccordionPanel>
                  <JailSectionPanel
                    jailName={jailName}
                    section={section}
                    onChange={(next) => { handleSectionChange(jailName, next); }}
                  />
                </AccordionPanel>
              </AccordionItem>
            );
          })}
        </Accordion>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// JailFileForm — public component
// ---------------------------------------------------------------------------

interface JailFileFormProps {
  filename: string;
}

/**
 * Load and render a structured form editor for a ``jail.d/*.conf`` file.
 *
 * @param filename - Filename including extension (e.g. ``"sshd.conf"``).
 */
export function JailFileForm({ filename }: JailFileFormProps): React.JSX.Element {
  const { config, loading, error, save } = useJailFileConfig(filename);

  if (loading) {
    return (
      <Skeleton aria-label="Loading jail file config…">
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: 8 }}>
          <SkeletonItem size={32} />
          <SkeletonItem size={32} />
          <SkeletonItem size={32} />
          <SkeletonItem size={32} />
        </div>
        <SkeletonItem size={32} />
      </Skeleton>
    );
  }

  if (error !== null) {
    return (
      <MessageBar intent="error" style={{ margin: "8px 0" }}>
        <MessageBarBody>{error}</MessageBarBody>
      </MessageBar>
    );
  }

  if (config === null) {
    return <></>;
  }

  return <JailFileFormInner config={config} onSave={save} />;
}
