/**
 * Tests for the redesigned FiltersTab component (Task 2.3).
 */

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

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

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

vi.mock("../../../api/file_config", () => ({
  fetchFilterFile: vi.fn(),
  updateFilterFile: vi.fn(),
}));

// Mock child components to isolate FiltersTab rendering.
vi.mock("../FilterForm", () => ({
  FilterForm: ({ name }: { name: string }) => (
    <div data-testid={`filter-form-${name}`}>FilterForm:{name}</div>
  ),
}));
vi.mock("../RawConfigSection", () => ({
  RawConfigSection: () => <div data-testid="raw-config-section">RawConfig</div>,
}));
vi.mock("../AssignFilterDialog", () => ({
  AssignFilterDialog: ({ open }: { open: boolean }) =>
    open ? <div data-testid="assign-dialog">AssignDialog</div> : null,
}));
vi.mock("../CreateFilterDialog", () => ({
  CreateFilterDialog: ({ open }: { open: boolean }) =>
    open ? <div data-testid="create-dialog">CreateDialog</div> : null,
}));

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

const mockFetchFilters = vi.mocked(fetchFilters);

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

const activeFilter: FilterConfig = {
  name: "sshd",
  filename: "sshd.conf",
  before: null,
  after: null,
  variables: {},
  prefregex: null,
  failregex: ["^<HOST> port \\d+"],
  ignoreregex: [],
  maxlines: null,
  datepattern: null,
  journalmatch: null,
  active: true,
  used_by_jails: ["sshd"],
  source_file: "/etc/fail2ban/filter.d/sshd.conf",
  has_local_override: false,
};

const inactiveFilter: FilterConfig = {
  name: "apache-auth",
  filename: "apache-auth.conf",
  before: null,
  after: null,
  variables: {},
  prefregex: null,
  failregex: [],
  ignoreregex: [],
  maxlines: null,
  datepattern: null,
  journalmatch: null,
  active: false,
  used_by_jails: [],
  source_file: "/etc/fail2ban/filter.d/apache-auth.conf",
  has_local_override: false,
};

const mockListResponse: FilterListResponse = {
  filters: [activeFilter, inactiveFilter],
  total: 2,
};

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

function renderTab() {
  return render(
    <FluentProvider theme={webLightTheme}>
      <FiltersTab />
    </FluentProvider>,
  );
}

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

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

  it("shows a loading skeleton while fetching filters", () => {
    // Never resolve the promise so loading stays true.
    mockFetchFilters.mockReturnValue(new Promise(() => undefined));

    renderTab();

    expect(screen.getByLabelText(/loading filters/i)).toBeInTheDocument();
  });

  it("shows an error message when fetchFilters fails", async () => {
    mockFetchFilters.mockRejectedValue(new Error("Network error"));

    renderTab();

    await waitFor(() => {
      expect(screen.getByText(/network error/i)).toBeInTheDocument();
    });
  });

  it("renders filter items after successful load", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      expect(screen.getByText("sshd")).toBeInTheDocument();
      expect(screen.getByText("apache-auth")).toBeInTheDocument();
    });
  });

  it("shows Active badge text for active filters", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      // Active filter used by "sshd" jail should show "Active — sshd"
      expect(screen.getByText(/active — sshd/i)).toBeInTheDocument();
    });
  });

  it("shows Inactive badge text for inactive filters", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      expect(screen.getByText(/inactive/i)).toBeInTheDocument();
    });
  });

  it("shows Create Filter button", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      expect(
        screen.getByRole("button", { name: /create filter/i }),
      ).toBeInTheDocument();
    });
  });

  it("shows placeholder when no filter is selected", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      expect(
        screen.getByText(/select an item from the list/i),
      ).toBeInTheDocument();
    });
  });

  it("shows FilterForm when a filter is selected", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    const { getByText } = renderTab();

    await waitFor(() => {
      expect(getByText("sshd")).toBeInTheDocument();
    });

    getByText("sshd").click();

    await waitFor(() => {
      expect(screen.getByTestId("filter-form-sshd")).toBeInTheDocument();
    });
  });

  it("shows AssignFilterDialog when Assign to Jail is clicked", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    const { getByText } = renderTab();

    await waitFor(() => {
      expect(getByText("sshd")).toBeInTheDocument();
    });

    getByText("sshd").click();

    await waitFor(() => {
      expect(
        screen.getByRole("button", { name: /assign to jail/i }),
      ).toBeInTheDocument();
    });

    screen.getByRole("button", { name: /assign to jail/i }).click();

    await waitFor(() => {
      expect(screen.getByTestId("assign-dialog")).toBeInTheDocument();
    });
  });

  it("calls fetchFilters once on mount", async () => {
    mockFetchFilters.mockResolvedValue(mockListResponse);

    renderTab();

    await waitFor(() => {
      expect(mockFetchFilters).toHaveBeenCalledTimes(1);
    });
  });
});
