/**
 * AutoSaveIndicator — small status chip showing auto-save state.
 *
 * Displays nothing while idle, a spinner while saving, a transient
 * "Saved" checkmark on success, and an error message with retry on failure.
 *
 * Always renders an ``aria-live`` region so screen readers receive
 * status change announcements even when the visible badge is absent.
 */

import { useEffect, useState } from "react";
import {
  Badge,
  Button,
  Spinner,
  Text,
  makeStyles,
  mergeClasses,
  tokens,
} from "@fluentui/react-components";
import { Checkmark16Regular } from "@fluentui/react-icons";

/** Current save state driven by the parent (typically useAutoSave). */
export type AutoSaveStatus = "idle" | "saving" | "saved" | "error";

export interface AutoSaveIndicatorProps {
  /** Current save status. */
  status: AutoSaveStatus;
  /** Error text shown when status is "error". */
  errorText?: string | null;
  /** Called when the user clicks the retry link. */
  onRetry?: () => void;
}

/** Fade-out delay after "saved" status in milliseconds. */
const SAVED_FADE_DELAY_MS = 2000;

const useStyles = makeStyles({
  root: {
    display: "inline-flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalXS,
    minWidth: "80px",
  },
  savingText: {
    color: tokens.colorNeutralForeground2,
  },
  errorText: {
    color: tokens.colorPaletteRedForeground3,
  },
  savedMotion: {
    transition: `opacity ${tokens.durationNormal} ${tokens.curveDecelerateMid}, transform ${tokens.durationNormal} ${tokens.curveDecelerateMid}`,
    animationName: "fadeInScale",
    animationDuration: tokens.durationFast,
    animationTimingFunction: tokens.curveDecelerateMid,
    animationFillMode: "both",
  },
  savedHidden: {
    opacity: 0,
    transform: "scale(0.95)",
  },
});

/**
 * Compact inline indicator for auto-save state.
 *
 * @param props - Component props.
 * @returns JSX element.
 */
export function AutoSaveIndicator({
  status,
  errorText,
  onRetry,
}: AutoSaveIndicatorProps): React.JSX.Element {
  const styles = useStyles();
  const [fadingOut, setFadingOut] = useState(false);

  // Trigger the fade-out transition 2 s after the saved state is reached.
  useEffect((): (() => void) | undefined => {
    if (status !== "saved") {
      setFadingOut(false);
      return undefined;
    }
    const timer = window.setTimeout(() => {
      setFadingOut(true);
    }, SAVED_FADE_DELAY_MS);
    return () => {
      window.clearTimeout(timer);
    };
  }, [status]);

  // Always render the aria-live region so screen readers track changes.
  return (
    <span aria-live="polite" role="status" className={styles.root}>
      {status === "saving" && (
        <>
          <Spinner size="extra-tiny" />
          <Text size={200} className={styles.savingText}>
            Saving…
          </Text>
        </>
      )}

      {status === "saved" && (
        <span
          className={mergeClasses(
            styles.savedMotion,
            fadingOut ? styles.savedHidden : undefined,
          )}
        >
          <Badge
            appearance="tint"
            color="success"
            icon={<Checkmark16Regular />}
            size="small"
          >
            Saved
          </Badge>
        </span>
      )}

      {status === "error" && (
        <>
          <Text size={200} className={styles.errorText}>
            {errorText ?? "Save failed."}
          </Text>
          {onRetry && (
            <Button appearance="transparent" size="small" onClick={onRetry}>
              Retry
            </Button>
          )}
        </>
      )}
    </span>
  );
}
