import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { render, screen, act } from "~/test-utils.js";
import { SuccessToast } from "./SuccessToast.js";

// Mock framer-motion so animations resolve synchronously in jsdom.
vi.mock("framer-motion", () => ({
  motion: {
    div: ({
      children,
      // strip animation props that are irrelevant in jsdom
      initial: _initial,
      animate: _animate,
      exit: _exit,
      transition: _transition,
      ...props
    }: React.HTMLAttributes<HTMLDivElement> & {
      children?: React.ReactNode;
      initial?: unknown;
      animate?: unknown;
      exit?: unknown;
      transition?: unknown;
    }) => <div {...props}>{children}</div>,
  },
  AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));

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

const AUTO_DISMISS_MS = 2500;

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe("SuccessToast", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.runOnlyPendingTimers();
    vi.useRealTimers();
  });

  // -------------------------------------------------------------------------
  // Visibility
  // -------------------------------------------------------------------------

  describe("visibility", () => {
    it("renders the message when show is true", () => {
      render(<SuccessToast show message="Saved!" />);
      expect(screen.getByText("Saved!")).toBeInTheDocument();
    });

    it("does not render when show is false", () => {
      render(<SuccessToast show={false} message="Saved!" />);
      expect(screen.queryByText("Saved!")).not.toBeInTheDocument();
    });

    it("disappears when show transitions from true to false", () => {
      const { rerender } = render(<SuccessToast show message="Saved!" />);
      expect(screen.getByText("Saved!")).toBeInTheDocument();
      rerender(<SuccessToast show={false} message="Saved!" />);
      expect(screen.queryByText("Saved!")).not.toBeInTheDocument();
    });

    it("appears when show transitions from false to true", () => {
      const { rerender } = render(<SuccessToast show={false} message="Now visible" />);
      expect(screen.queryByText("Now visible")).not.toBeInTheDocument();
      rerender(<SuccessToast show message="Now visible" />);
      expect(screen.getByText("Now visible")).toBeInTheDocument();
    });
  });

  // -------------------------------------------------------------------------
  // Auto-dismiss timer
  // -------------------------------------------------------------------------

  describe("auto-dismiss timer", () => {
    it("calls onDone after 2500 ms when show is true", () => {
      const onDone = vi.fn();
      render(<SuccessToast show message="Done" onDone={onDone} />);
      expect(onDone).not.toHaveBeenCalled();
      act(() => {
        vi.advanceTimersByTime(AUTO_DISMISS_MS);
      });
      expect(onDone).toHaveBeenCalledOnce();
    });

    it("does NOT call onDone before 2500 ms have passed", () => {
      const onDone = vi.fn();
      render(<SuccessToast show message="Done" onDone={onDone} />);
      act(() => {
        vi.advanceTimersByTime(AUTO_DISMISS_MS - 1);
      });
      expect(onDone).not.toHaveBeenCalled();
    });

    it("does NOT call onDone when show is false", () => {
      const onDone = vi.fn();
      render(<SuccessToast show={false} message="Done" onDone={onDone} />);
      act(() => {
        vi.advanceTimersByTime(AUTO_DISMISS_MS * 2);
      });
      expect(onDone).not.toHaveBeenCalled();
    });

    it("does not throw when onDone is omitted", () => {
      expect(() => {
        render(<SuccessToast show message="No callback" />);
        act(() => {
          vi.advanceTimersByTime(AUTO_DISMISS_MS);
        });
      }).not.toThrow();
    });

    it("resets the timer when show flips from false to true", () => {
      const onDone = vi.fn();
      const { rerender } = render(<SuccessToast show={false} message="Msg" onDone={onDone} />);
      act(() => {
        vi.advanceTimersByTime(1000);
      });
      rerender(<SuccessToast show message="Msg" onDone={onDone} />);
      // Timer starts fresh from the rerender — onDone should fire after another 2500 ms.
      act(() => {
        vi.advanceTimersByTime(AUTO_DISMISS_MS - 1);
      });
      expect(onDone).not.toHaveBeenCalled();
      act(() => {
        vi.advanceTimersByTime(1);
      });
      expect(onDone).toHaveBeenCalledOnce();
    });

    it("clears the pending timer when show changes to false before 2500 ms", () => {
      const onDone = vi.fn();
      const { rerender } = render(<SuccessToast show message="Msg" onDone={onDone} />);
      act(() => {
        vi.advanceTimersByTime(1000);
      });
      rerender(<SuccessToast show={false} message="Msg" onDone={onDone} />);
      act(() => {
        vi.advanceTimersByTime(AUTO_DISMISS_MS);
      });
      // onDone should NOT have been called because the timer was cleared.
      expect(onDone).not.toHaveBeenCalled();
    });
  });

  // -------------------------------------------------------------------------
  // Variants
  // -------------------------------------------------------------------------

  describe("variants", () => {
    it("defaults to the 'success' variant and renders the check-mark path", () => {
      const { container } = render(<SuccessToast show message="OK" />);
      // The success icon has the path "M5 13l4 4L19 7"
      const path = container.querySelector('path[d="M5 13l4 4L19 7"]');
      expect(path).not.toBeNull();
    });

    it("renders the 'info' variant icon (circle element)", () => {
      const { container } = render(<SuccessToast show message="Info" variant="info" />);
      // The info icon contains a <circle> element
      expect(container.querySelector("circle")).not.toBeNull();
    });

    it("renders the 'warning' variant icon (triangle path)", () => {
      const { container } = render(<SuccessToast show message="Warn" variant="warning" />);
      // The warning icon path contains the triangle shape with "ZM12 15.75"
      const path = container.querySelector('path[d*="ZM12 15.75"]');
      expect(path).not.toBeNull();
    });

    it("applies success background class for the 'success' variant", () => {
      const { container } = render(<SuccessToast show message="OK" variant="success" />);
      const inner = container.querySelector(".flex.items-center") as HTMLElement;
      expect(inner.className).toContain("emerald");
    });

    it("applies info background class for the 'info' variant", () => {
      const { container } = render(<SuccessToast show message="Info" variant="info" />);
      const inner = container.querySelector(".flex.items-center") as HTMLElement;
      expect(inner.className).toContain("blue");
    });

    it("applies warning background class for the 'warning' variant", () => {
      const { container } = render(<SuccessToast show message="Warn" variant="warning" />);
      const inner = container.querySelector(".flex.items-center") as HTMLElement;
      expect(inner.className).toContain("amber");
    });
  });

  // -------------------------------------------------------------------------
  // Message rendering
  // -------------------------------------------------------------------------

  describe("message", () => {
    it("renders the exact message string", () => {
      render(<SuccessToast show message="Profile updated successfully" />);
      expect(screen.getByText("Profile updated successfully")).toBeInTheDocument();
    });

    it("updates the message text when the prop changes", () => {
      const { rerender } = render(<SuccessToast show message="First" />);
      expect(screen.getByText("First")).toBeInTheDocument();
      rerender(<SuccessToast show message="Second" />);
      expect(screen.getByText("Second")).toBeInTheDocument();
      expect(screen.queryByText("First")).not.toBeInTheDocument();
    });

    it("message is wrapped in a <span> with font-medium class", () => {
      render(<SuccessToast show message="Check me" />);
      const span = screen.getByText("Check me");
      expect(span.tagName.toLowerCase()).toBe("span");
      expect(span.className).toContain("font-medium");
    });
  });
});
