/**
 * ActionsTab — master/detail layout for action.d discovery and editing.
 *
 * Left pane: action names with Active/Inactive badges (active actions sorted
 * to the top).  Active badge also shows which jails reference the action.
 * Right pane: structured action editor, "Assign to Jail" button, a
 * "Remove from Jail" section, and a collapsible raw-config editor.
 *
 * A "Create Action" button at the top of the list pane opens a dialog for
 * creating a new ``action.d/*.local`` file.
 */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  Button,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Skeleton,
  SkeletonItem,
  tokens,
} from "@fluentui/react-components";
import { Add24Regular, Delete24Regular, LinkEdit24Regular } from "@fluentui/react-icons";
import {
  fetchActionFile,
  fetchActions,
  removeActionFromJail,
  updateActionFile,
} from "../../api/config";
import type { ActionConfig, ConfFileUpdateRequest } from "../../types/config";
import { ActionForm } from "./ActionForm";
import { AssignActionDialog } from "./AssignActionDialog";
import { ConfigListDetail } from "./ConfigListDetail";
import { CreateActionDialog } from "./CreateActionDialog";
import { RawConfigSection } from "./RawConfigSection";
import { useConfigStyles } from "./configStyles";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/**
 * Build the badge label text shown next to each action in the list pane.
 */
function actionBadgeLabel(a: ActionConfig): string {
  if (!a.active) return "Inactive";
  if (a.used_by_jails.length === 0) return "Active";
  return `Active — ${a.used_by_jails.join(", ")}`;
}

// ---------------------------------------------------------------------------
// ActionDetail — right-pane detail for a selected action
// ---------------------------------------------------------------------------

interface ActionDetailProps {
  action: ActionConfig;
  onAssignClick: () => void;
  onRemovedFromJail: () => void;
}

/**
 * Detail pane for a selected action: shows meta information, the structured
 * editor, assign/remove jail actions, and a raw-config section.
 *
 * @param props - Component props.
 * @returns JSX element.
 */
function ActionDetail({
  action,
  onAssignClick,
  onRemovedFromJail,
}: ActionDetailProps): React.JSX.Element {
  const styles = useConfigStyles();
  const [removingJail, setRemovingJail] = useState<string | null>(null);
  const [removeError, setRemoveError] = useState<string | null>(null);

  const fetchRaw = useCallback(async (): Promise<string> => {
    const result = await fetchActionFile(action.name);
    return result.content;
  }, [action.name]);

  const saveRaw = useCallback(
    async (content: string): Promise<void> => {
      const req: ConfFileUpdateRequest = { content };
      await updateActionFile(action.name, req);
    },
    [action.name],
  );

  const handleRemoveFromJail = useCallback(
    (jailName: string): void => {
      setRemovingJail(jailName);
      setRemoveError(null);
      removeActionFromJail(jailName, action.name)
        .then(() => {
          onRemovedFromJail();
        })
        .catch((err: unknown) => {
          setRemoveError(
            err instanceof Error ? err.message : "Failed to remove action from jail.",
          );
        })
        .finally(() => {
          setRemovingJail(null);
        });
    },
    [action.name, onRemovedFromJail],
  );

  return (
    <div>
      {/* Meta information row */}
      <div className={styles.fieldRow} style={{ marginBottom: tokens.spacingVerticalS }}>
        <Field label="Source file">
          <Input
            readOnly
            value={action.source_file || action.filename}
            className={styles.codeInput}
            size="small"
          />
        </Field>
      </div>

      {/* Structured editor */}
      <ActionForm name={action.name} />

      {/* Assign to jail action */}
      <div style={{ marginTop: tokens.spacingVerticalM }}>
        <Button
          appearance="secondary"
          icon={<LinkEdit24Regular />}
          onClick={onAssignClick}
        >
          Assign to Jail
        </Button>
      </div>

      {/* Remove from jail section */}
      {action.used_by_jails.length > 0 && (
        <div style={{ marginTop: tokens.spacingVerticalM }}>
          {removeError !== null && (
            <MessageBar
              intent="error"
              style={{ marginBottom: tokens.spacingVerticalXS }}
            >
              <MessageBarBody>{removeError}</MessageBarBody>
            </MessageBar>
          )}
          <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            {action.used_by_jails.map((jailName) => (
              <div
                key={jailName}
                style={{ display: "flex", alignItems: "center", gap: 8 }}
              >
                <span style={{ fontFamily: "monospace", fontSize: 13 }}>
                  {jailName}
                </span>
                <Button
                  appearance="subtle"
                  size="small"
                  icon={<Delete24Regular />}
                  disabled={removingJail !== null}
                  onClick={() => { handleRemoveFromJail(jailName); }}
                  aria-label={`Remove action from ${jailName}`}
                >
                  Remove
                </Button>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Raw config */}
      <div style={{ marginTop: tokens.spacingVerticalL }}>
        <RawConfigSection
          fetchContent={fetchRaw}
          saveContent={saveRaw}
          label="Raw Action Configuration"
        />
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// ActionsTab (public export)
// ---------------------------------------------------------------------------

/**
 * Tab component for exploring and editing fail2ban action definitions.
 *
 * @returns JSX element.
 */
export function ActionsTab(): React.JSX.Element {
  const [actions, setActions] = useState<ActionConfig[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [selectedName, setSelectedName] = useState<string | null>(null);
  const [assignOpen, setAssignOpen] = useState(false);
  const [createOpen, setCreateOpen] = useState(false);
  const abortRef = useRef<AbortController | null>(null);

  const loadActions = useCallback((): void => {
    abortRef.current?.abort();
    const ctrl = new AbortController();
    abortRef.current = ctrl;
    setLoading(true);
    setError(null);

    fetchActions()
      .then((resp) => {
        if (!ctrl.signal.aborted) {
          setActions(resp.actions);
          setLoading(false);
        }
      })
      .catch((err: unknown) => {
        if (!ctrl.signal.aborted) {
          setError(err instanceof Error ? err.message : "Failed to load actions");
          setLoading(false);
        }
      });
  }, []);

  useEffect(() => {
    loadActions();
    return (): void => {
      abortRef.current?.abort();
    };
  }, [loadActions]);

  /** The full ActionConfig for the currently selected name. */
  const selectedAction = useMemo(
    () => actions.find((a) => a.name === selectedName) ?? null,
    [actions, selectedName],
  );

  const handleAssigned = useCallback((): void => {
    setAssignOpen(false);
    // Refresh action list so active status is up-to-date.
    loadActions();
  }, [loadActions]);

  const handleCreated = useCallback(
    (newAction: ActionConfig): void => {
      setCreateOpen(false);
      setActions((prev) => [...prev, newAction]);
      setSelectedName(newAction.name);
    },
    [],
  );

  const handleRemovedFromJail = useCallback((): void => {
    loadActions();
  }, [loadActions]);

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

  if (error) {
    return (
      <MessageBar intent="error">
        <MessageBarBody>{error}</MessageBarBody>
      </MessageBar>
    );
  }

  const listHeader = (
    <Button
      appearance="outline"
      icon={<Add24Regular />}
      size="small"
      onClick={() => { setCreateOpen(true); }}
    >
      Create Action
    </Button>
  );

  return (
    <>
      <ConfigListDetail
        items={actions}
        isActive={(a) => a.active}
        itemBadgeLabel={actionBadgeLabel}
        selectedName={selectedName}
        onSelect={setSelectedName}
        loading={false}
        error={null}
        listHeader={listHeader}
      >
        {selectedAction !== null && (
          <ActionDetail
            key={selectedAction.name}
            action={selectedAction}
            onAssignClick={() => { setAssignOpen(true); }}
            onRemovedFromJail={handleRemovedFromJail}
          />
        )}
      </ConfigListDetail>

      <AssignActionDialog
        actionName={selectedName}
        open={assignOpen}
        onClose={() => { setAssignOpen(false); }}
        onAssigned={handleAssigned}
      />

      <CreateActionDialog
        open={createOpen}
        onClose={() => { setCreateOpen(false); }}
        onCreate={handleCreated}
      />
    </>
  );
}

