import { beforeEach, describe, expect, it, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";

interface MockButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
}

interface MockBadgeProps extends HTMLAttributes<HTMLElement> {
  children: ReactNode;
}

interface AuditLogEntry {
  id: string;
  userId: string;
  sessionId: string;
  provider: string;
  action: string;
  content: string | null;
  metadata: unknown;
  createdAt: string;
}

interface AuditLogResponse {
  items: AuditLogEntry[];
  total: number;
  page: number;
  pages: number;
}

const mockApiGet = vi.fn<(endpoint: string) => Promise<AuditLogResponse>>();

vi.mock("@/lib/api/client", () => ({
  apiGet: (endpoint: string): Promise<AuditLogResponse> => mockApiGet(endpoint),
}));

vi.mock("@/components/ui/button", () => ({
  Button: ({ children, ...props }: MockButtonProps): React.JSX.Element => (
    <button {...props}>{children}</button>
  ),
}));

vi.mock("@/components/ui/badge", () => ({
  Badge: ({ children, ...props }: MockBadgeProps): React.JSX.Element => (
    <span {...props}>{children}</span>
  ),
}));

import { AuditLogDrawer } from "./AuditLogDrawer";

function renderWithQueryClient(ui: React.JSX.Element): ReturnType<typeof render> {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
}

function responseWith(items: AuditLogEntry[], page: number, pages: number): AuditLogResponse {
  return {
    items,
    total: items.length,
    page,
    pages,
  };
}

describe("AuditLogDrawer", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
    mockApiGet.mockResolvedValue(responseWith([], 1, 0));
  });

  it("opens from trigger text and renders empty state", async (): Promise<void> => {
    const user = userEvent.setup();

    renderWithQueryClient(<AuditLogDrawer trigger="Audit" />);

    await user.click(screen.getByRole("button", { name: "Audit" }));

    await waitFor((): void => {
      expect(screen.getByText("Audit Log")).toBeInTheDocument();
      expect(screen.getByText("No audit entries found.")).toBeInTheDocument();
    });
  });

  it("renders audit entries with action, session id, and payload", async (): Promise<void> => {
    const user = userEvent.setup();

    mockApiGet.mockResolvedValue(
      responseWith(
        [
          {
            id: "entry-1",
            userId: "operator-1",
            sessionId: "1234567890abcdef",
            provider: "internal",
            action: "inject",
            content: "Run diagnostics",
            metadata: { payload: { ignored: true } },
            createdAt: "2026-03-07T19:00:00.000Z",
          },
        ],
        1,
        1
      )
    );

    renderWithQueryClient(<AuditLogDrawer trigger="Audit" />);

    await user.click(screen.getByRole("button", { name: "Audit" }));

    await waitFor((): void => {
      expect(screen.getByText("inject")).toBeInTheDocument();
      expect(screen.getByText("12345678")).toBeInTheDocument();
      expect(screen.getByText("Run diagnostics")).toBeInTheDocument();
    });
  });

  it("supports pagination and metadata payload summary", async (): Promise<void> => {
    const user = userEvent.setup();

    mockApiGet.mockImplementation((endpoint: string): Promise<AuditLogResponse> => {
      const query = endpoint.split("?")[1] ?? "";
      const params = new URLSearchParams(query);
      const page = Number(params.get("page") ?? "1");

      if (page === 1) {
        return Promise.resolve({
          items: [
            {
              id: "entry-page-1",
              userId: "operator-2",
              sessionId: "abcdefgh12345678",
              provider: "internal",
              action: "pause",
              content: "",
              metadata: { payload: { reason: "hold" } },
              createdAt: "2026-03-07T19:01:00.000Z",
            },
          ],
          total: 2,
          page: 1,
          pages: 2,
        });
      }

      return Promise.resolve({
        items: [
          {
            id: "entry-page-2",
            userId: "operator-3",
            sessionId: "zzzz111122223333",
            provider: "internal",
            action: "kill",
            content: null,
            metadata: { payload: { force: true } },
            createdAt: "2026-03-07T19:02:00.000Z",
          },
        ],
        total: 2,
        page: 2,
        pages: 2,
      });
    });

    renderWithQueryClient(<AuditLogDrawer trigger="Audit" />);

    await user.click(screen.getByRole("button", { name: "Audit" }));

    await waitFor((): void => {
      expect(screen.getByText("Page 1 of 2")).toBeInTheDocument();
      expect(screen.getByText("reason=hold")).toBeInTheDocument();
    });

    await user.click(screen.getByRole("button", { name: "Next" }));

    await waitFor((): void => {
      expect(screen.getByText("Page 2 of 2")).toBeInTheDocument();
      expect(screen.getByText("force=true")).toBeInTheDocument();
    });
  });

  it("includes sessionId filter in query string", async (): Promise<void> => {
    const user = userEvent.setup();

    renderWithQueryClient(<AuditLogDrawer trigger="Audit" sessionId="session 7" />);

    await user.click(screen.getByRole("button", { name: "Audit" }));

    await waitFor((): void => {
      expect(mockApiGet).toHaveBeenCalled();
    });

    const firstCall = mockApiGet.mock.calls[0];
    const endpoint = firstCall?.[0] ?? "";

    expect(endpoint).toContain("sessionId=session+7");
  });
});
