/**
 * RawConfigSection — collapsible raw-text editor for .conf files.
 *
 * Renders a single Accordion item.  On first expand the raw content is
 * fetched; the user can edit the textarea and save with a primary button.
 * Feedback is shown via an {@link AutoSaveIndicator}-style message bar.
 */

import { useCallback, useRef, useState } from "react";
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  Button,
  MessageBar,
  MessageBarBody,
  Spinner,
  Textarea,
  makeStyles,
  tokens,
} from "@fluentui/react-components";
import { AutoSaveIndicator } from "./AutoSaveIndicator";

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

export interface RawConfigSectionProps {
  /** Async function that returns the raw file content string. */
  fetchContent: () => Promise<string>;
  /** Async function that saves updated raw content. */
  saveContent: (content: string) => Promise<void>;
  /** Label shown in the collapsible header, e.g. "Raw Jail Configuration". */
  label?: string;
}

/** Minimum visible rows for the monospace text area. */
const MIN_ROWS = 15;

const useStyles = makeStyles({
  headerText: {
    fontWeight: tokens.fontWeightSemibold,
  },
  loadingRow: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
    padding: tokens.spacingVerticalM,
  },
  loadingText: {
    color: tokens.colorNeutralForeground3,
  },
  textArea: {
    fontFamily: tokens.fontFamilyMonospace,
    fontSize: tokens.fontSizeBase200,
    width: "100%",
    borderLeft: `3px solid ${tokens.colorBrandStroke1}`,
    marginBottom: tokens.spacingVerticalS,
  },
  controlsRow: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
    marginTop: tokens.spacingVerticalXS,
  },
  errorBar: {
    marginBottom: tokens.spacingVerticalS,
  },
});

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

/**
 * Collapsible section for viewing and editing raw .conf file content.
 *
 * @param props - Component props.
 * @returns JSX element.
 */
export function RawConfigSection({
  fetchContent,
  saveContent,
  label = "Raw Configuration",
}: RawConfigSectionProps): React.JSX.Element {
  const styles = useStyles();
  /** Raw text content; null means not yet loaded. */
  const [content, setContent] = useState<string | null>(null);
  const [localText, setLocalText] = useState("");
  const [fetchLoading, setFetchLoading] = useState(false);
  const [fetchError, setFetchError] = useState<string | null>(null);
  const [saveStatus, setSaveStatus] = useState<
    "idle" | "saving" | "saved" | "error"
  >("idle");
  const [saveError, setSaveError] = useState<string | null>(null);

  /** Whether the section has been expanded at least once. */
  const loadedRef = useRef(false);

  // --------------------------------------------------------------------------
  // Handlers
  // --------------------------------------------------------------------------

  /** Called when the accordion panel opens for the first time. */
  const handleExpand = useCallback(
    (_: React.SyntheticEvent, data: { openItems: string[] }): void => {
      const isOpen = data.openItems.includes("raw");
      if (!isOpen || loadedRef.current) return;
      loadedRef.current = true;

      setFetchLoading(true);
      setFetchError(null);

      fetchContent()
        .then((text) => {
          setContent(text);
          setLocalText(text);
          setFetchLoading(false);
        })
        .catch((err: unknown) => {
          setFetchError(
            err instanceof Error ? err.message : "Failed to load raw content.",
          );
          setFetchLoading(false);
        });
    },
    [fetchContent],
  );

  const handleSave = useCallback(async (): Promise<void> => {
    setSaveStatus("saving");
    setSaveError(null);
    try {
      await saveContent(localText);
      setContent(localText);
      setSaveStatus("saved");
    } catch (err: unknown) {
      const msg =
        err instanceof Error ? err.message : "Failed to save raw content.";
      setSaveError(msg);
      setSaveStatus("error");
    }
  }, [localText, saveContent]);

  const handleRetry = useCallback((): void => {
    void handleSave();
  }, [handleSave]);

  // --------------------------------------------------------------------------
  // Render
  // --------------------------------------------------------------------------

  return (
    <Accordion collapsible onToggle={handleExpand}>
      <AccordionItem value="raw">
        <AccordionHeader>
          <span className={styles.headerText}>{label}</span>
        </AccordionHeader>
        <AccordionPanel>
          {fetchLoading && (
            <div className={styles.loadingRow}>
              <Spinner size="tiny" />
              <span className={styles.loadingText}>
                Loading…
              </span>
            </div>
          )}

          {fetchError && (
            <MessageBar intent="error" className={styles.errorBar}>
              <MessageBarBody>{fetchError}</MessageBarBody>
            </MessageBar>
          )}

          {!fetchLoading && content !== null && (
            <>
              <Textarea
                value={localText}
                onChange={(_e, d) => {
                  setLocalText(d.value);
                }}
                resize="vertical"
                rows={MIN_ROWS}
                className={styles.textArea}
                aria-label={label}
              />
              <div className={styles.controlsRow}>
                <Button
                  appearance="primary"
                  onClick={() => {
                    void handleSave();
                  }}
                  disabled={saveStatus === "saving"}
                >
                  Save Raw
                </Button>
                <AutoSaveIndicator
                  status={saveStatus}
                  errorText={saveError}
                  onRetry={handleRetry}
                />
              </div>
            </>
          )}
        </AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}
