import React from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import { api } from "./api";
import NotificationsPopover from "./components/NotificationsPopover";
import { I18nProvider } from "./i18n/I18nProvider";

jest.mock("./api", () => ({
  api: {
    get: jest.fn(),
    post: jest.fn(),
    delete: jest.fn(),
    interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
  },
  getApiErrorMessage: (_error: unknown, fallback?: string) => fallback || "Request failed.",
}));

const mockedApi = api as jest.Mocked<typeof api>;
const notification = {
  id: "22222222-2222-2222-2222-222222222222",
  operationId: null,
  kind: "operation_failed",
  title: "CV export failed",
  message: "Review the operation and try again.",
  linkPath: "/operations",
  createdAtUtc: "2026-08-15T12:00:00Z",
  readAtUtc: null,
};

function renderPopover(overrides: Partial<React.ComponentProps<typeof NotificationsPopover>> = {}) {
  const anchor = document.createElement("button");
  document.body.appendChild(anchor);
  const props = {
    anchorEl: anchor,
    onClose: jest.fn(),
    onNavigate: jest.fn(),
    onChanged: jest.fn(),
    ...overrides,
  };
  const result = render(<I18nProvider><NotificationsPopover {...props} /></I18nProvider>);
  return { ...result, props, anchor };
}

beforeEach(() => {
  jest.clearAllMocks();
  mockedApi.get.mockResolvedValue({ data: [notification] } as any);
  mockedApi.post.mockResolvedValue({ data: {} } as any);
  mockedApi.delete.mockResolvedValue({ data: {} } as any);
});

test("shows notifications and updates read and dismissed state in place", async () => {
  const { props } = renderPopover();

  expect(await screen.findByText("CV export failed")).toBeInTheDocument();
  expect(mockedApi.get).toHaveBeenCalledWith("/notifications?limit=10");

  fireEvent.click(screen.getByRole("button", { name: "Mark read" }));
  await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(`/notifications/${notification.id}/read`));
  expect(props.onChanged).toHaveBeenCalledTimes(1);
  await waitFor(() => expect(screen.queryByRole("button", { name: "Mark read" })).not.toBeInTheDocument());

  fireEvent.click(screen.getByRole("button", { name: "Dismiss" }));
  await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith(`/notifications/${notification.id}`));
  await waitFor(() => expect(screen.queryByText("CV export failed")).not.toBeInTheDocument());
  expect(props.onChanged).toHaveBeenCalledTimes(2);
});

test("opens a notification destination instead of routing the bell itself", async () => {
  const { props } = renderPopover();

  fireEvent.click(await screen.findByRole("button", { name: "Open" }));

  await waitFor(() => expect(props.onNavigate).toHaveBeenCalledWith("/operations"));
  expect(mockedApi.post).toHaveBeenCalledWith(`/notifications/${notification.id}/read`);
  expect(props.onClose).toHaveBeenCalledTimes(1);
});

test("shows an honest empty state", async () => {
  mockedApi.get.mockResolvedValue({ data: [] } as any);
  renderPopover();

  expect(await screen.findByText("You're all caught up")).toBeInTheDocument();
});
