/**
 * Tests for the CreateFilterDialog component (Task 2.3).
 */

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { CreateFilterDialog } from "../CreateFilterDialog";
import type { FilterConfig } from "../../../types/config";

// ---------------------------------------------------------------------------
// Mocks
// ---------------------------------------------------------------------------

vi.mock("../../../api/config", () => ({
  createFilter: vi.fn(),
}));

import { createFilter } from "../../../api/config";

const mockCreateFilter = vi.mocked(createFilter);

// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------

const createdFilter: FilterConfig = {
  name: "my-app",
  filename: "my-app.local",
  before: null,
  after: null,
  variables: {},
  prefregex: null,
  failregex: ["^<HOST> failed"],
  ignoreregex: [],
  maxlines: null,
  datepattern: null,
  journalmatch: null,
  active: false,
  used_by_jails: [],
  source_file: "/etc/fail2ban/filter.d/my-app.local",
  has_local_override: true,
};

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

function renderDialog(
  overrides: Partial<React.ComponentProps<typeof CreateFilterDialog>> = {},
) {
  const props = {
    open: true,
    onClose: vi.fn(),
    onCreateFilter: vi.fn(async () => createdFilter),
    onCreate: vi.fn(),
    ...overrides,
  };
  return render(
    <FluentProvider theme={webLightTheme}>
      <CreateFilterDialog {...props} />
    </FluentProvider>,
  );
}

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

describe("CreateFilterDialog", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("renders dialog title when open", () => {
    renderDialog();
    expect(screen.getByRole("heading", { name: /create filter/i })).toBeInTheDocument();
  });

  it("does not render when closed", () => {
    renderDialog({ open: false });
    expect(screen.queryByText(/create filter/i)).not.toBeInTheDocument();
  });

  it("Create Filter button is disabled when name is empty", () => {
    renderDialog();
    // Name field is empty by default.
    expect(
      screen.getByRole("button", { name: /create filter/i }),
    ).toBeDisabled();
  });

  it("Create Filter button is enabled after entering a name", () => {
    renderDialog();

    const nameInput = screen.getByRole("textbox", { name: /filter name/i });
    fireEvent.change(nameInput, { target: { value: "my-app" } });

    expect(
      screen.getByRole("button", { name: /create filter/i }),
    ).not.toBeDisabled();
  });

  it("calls onClose when Cancel is clicked", () => {
    const onClose = vi.fn();
    renderDialog({ onClose });

    fireEvent.click(screen.getByRole("button", { name: /cancel/i }));
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("calls createFilter and onCreate on submit", async () => {
    const onCreate = vi.fn();
    mockCreateFilter.mockResolvedValue(createdFilter);

    renderDialog({ onCreate });

    const nameInput = screen.getByRole("textbox", { name: /filter name/i });
    fireEvent.change(nameInput, { target: { value: "my-app" } });

    fireEvent.click(screen.getByRole("button", { name: /create filter/i }));

    await waitFor(() => {
      expect(mockCreateFilter).toHaveBeenCalledWith({
        name: "my-app",
        failregex: [],
        ignoreregex: [],
      });
      expect(onCreate).toHaveBeenCalledWith(createdFilter);
    });
  });

  it("shows error message when createFilter fails", async () => {
    mockCreateFilter.mockRejectedValue(new Error("Name already exists"));

    renderDialog();

    const nameInput = screen.getByRole("textbox", { name: /filter name/i });
    fireEvent.change(nameInput, { target: { value: "sshd" } });

    fireEvent.click(screen.getByRole("button", { name: /create filter/i }));

    await waitFor(() => {
      expect(screen.getByText(/failed to create filter/i)).toBeInTheDocument();
    });
  });

  it("splits failregex textarea into lines on submit", async () => {
    const onCreate = vi.fn();
    mockCreateFilter.mockResolvedValue(createdFilter);

    renderDialog({ onCreate });

    const nameInput = screen.getByRole("textbox", { name: /filter name/i });
    fireEvent.change(nameInput, { target: { value: "my-app" } });

    const failregexArea = screen.getByRole("textbox", { name: /failregex/i });
    fireEvent.change(failregexArea, {
      target: { value: "^<HOST> failed\n^<HOST> error" },
    });

    fireEvent.click(screen.getByRole("button", { name: /create filter/i }));

    await waitFor(() => {
      expect(mockCreateFilter).toHaveBeenCalledWith(
        expect.objectContaining({
          failregex: ["^<HOST> failed", "^<HOST> error"],
        }),
      );
    });
  });
});
