import * as api from "@client/api";
import { renderWithQueryClient } from "@client/test/renderWithQueryClient";
import { createJob } from "@shared/testing/factories.js";
import type { Job } from "@shared/types.js";
import { fireEvent, screen, waitFor } from "@testing-library/react";
import type React from "react";
import { MemoryRouter } from "react-router-dom";
import { toast } from "sonner";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { DiscoveredPanel } from "./DiscoveredPanel";

const render = (ui: Parameters<typeof renderWithQueryClient>[0]) =>
  renderWithQueryClient(ui);

const mockSettings = {
  showSponsorInfo: false,
  renderMarkdownInJobDescriptions: true,
};

vi.mock("@/components/ui/dropdown-menu", () => {
  return {
    DropdownMenu: ({ children }: { children: React.ReactNode }) => (
      <div>{children}</div>
    ),
    DropdownMenuTrigger: ({ children }: { children: React.ReactNode }) => (
      <>{children}</>
    ),
    DropdownMenuContent: ({ children }: { children: React.ReactNode }) => (
      <div role="menu">{children}</div>
    ),
    DropdownMenuItem: ({
      children,
      onSelect,
      ...props
    }: {
      children: React.ReactNode;
      onSelect?: () => void;
    }) => (
      <button
        type="button"
        role="menuitem"
        onClick={() => onSelect?.()}
        {...props}
      >
        {children}
      </button>
    ),
    DropdownMenuSeparator: () => <hr />,
  };
});

vi.mock("@client/hooks/useSettings", () => ({
  useSettings: () => mockSettings,
}));

vi.mock("@client/api", () => ({
  rescoreJob: vi.fn(),
  skipJob: vi.fn(),
  processJob: vi.fn(),
  checkSponsor: vi.fn(),
}));

vi.mock("../JobDetailsEditDrawer", () => ({
  JobDetailsEditDrawer: ({
    open,
    onOpenChange,
    onJobUpdated,
  }: {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    onJobUpdated: () => void | Promise<void>;
  }) =>
    open ? (
      <div data-testid="job-details-edit-drawer">
        <button
          type="button"
          onClick={() => {
            void onJobUpdated();
            onOpenChange(false);
          }}
        >
          Save details
        </button>
      </div>
    ) : null,
}));

vi.mock("sonner", () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
    message: vi.fn(),
  },
}));

describe("DiscoveredPanel", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockSettings.showSponsorInfo = false;
    mockSettings.renderMarkdownInJobDescriptions = true;
  });

  it("re-runs the fit assessment from the menu", async () => {
    const onJobUpdated = vi.fn().mockResolvedValue(undefined);
    const job = createJob({ id: "job-2" });
    vi.mocked(api.rescoreJob).mockResolvedValue(job as Job);

    render(
      <MemoryRouter>
        <DiscoveredPanel
          job={job}
          onJobUpdated={onJobUpdated}
          onJobMoved={vi.fn()}
        />
      </MemoryRouter>,
    );

    fireEvent.click(
      screen.getByRole("menuitem", { name: /recalculate match/i }),
    );

    await waitFor(() => expect(api.rescoreJob).toHaveBeenCalledWith("job-2"));
    expect(onJobUpdated).toHaveBeenCalled();
    expect(toast.success).toHaveBeenCalledWith("Match recalculated");
  });

  it("opens edit details drawer from more actions", async () => {
    const onJobUpdated = vi.fn().mockResolvedValue(undefined);
    const job = createJob({ id: "job-2" });

    render(
      <MemoryRouter>
        <DiscoveredPanel
          job={job}
          onJobUpdated={onJobUpdated}
          onJobMoved={vi.fn()}
        />
      </MemoryRouter>,
    );

    fireEvent.click(screen.getByRole("menuitem", { name: /edit details/i }));
    expect(screen.getByTestId("job-details-edit-drawer")).toBeInTheDocument();

    fireEvent.click(screen.getByRole("button", { name: /save details/i }));
    await waitFor(() => expect(onJobUpdated).toHaveBeenCalled());
    expect(
      screen.queryByTestId("job-details-edit-drawer"),
    ).not.toBeInTheDocument();
  });

  it("shows an open job listing link when the discovered job has an external url", () => {
    const job = createJob({
      id: "job-3",
      jobUrl: "https://example.com/jobs/visit-me",
      applicationLink: null,
    });

    render(
      <MemoryRouter>
        <DiscoveredPanel
          job={job}
          onJobUpdated={vi.fn()}
          onJobMoved={vi.fn()}
        />
      </MemoryRouter>,
    );

    expect(
      screen.getByRole("link", { name: /open job listing/i }),
    ).toHaveAttribute("href", "https://example.com/jobs/visit-me");
  });

  it("renders markdown formatting in the expanded job description when markdown rendering is enabled", () => {
    const job = createJob({
      jobDescription:
        "# Responsibilities\n\n- Build APIs\n- Improve reliability",
    });

    render(
      <MemoryRouter>
        <DiscoveredPanel
          job={job}
          onJobUpdated={vi.fn()}
          onJobMoved={vi.fn()}
        />
      </MemoryRouter>,
    );

    fireEvent.click(
      screen.getByRole("button", { name: /view full job description/i }),
    );

    expect(
      screen.getByRole("heading", { name: "Responsibilities" }),
    ).toBeInTheDocument();
    expect(screen.getByText("Build APIs")).toBeInTheDocument();
    expect(screen.queryByText("# Responsibilities")).not.toBeInTheDocument();
  });

  it("renders raw markdown in the expanded job description when markdown rendering is disabled", () => {
    mockSettings.renderMarkdownInJobDescriptions = false;

    const job = createJob({
      jobDescription:
        "# Responsibilities\n\n- Build APIs\n- Improve reliability",
    });

    const rendered = render(
      <MemoryRouter>
        <DiscoveredPanel
          job={job}
          onJobUpdated={vi.fn()}
          onJobMoved={vi.fn()}
        />
      </MemoryRouter>,
    );

    fireEvent.click(
      screen.getByRole("button", { name: /view full job description/i }),
    );

    const rawDescription = rendered.container.querySelector(
      "p.whitespace-pre-wrap",
    );
    expect(rawDescription?.textContent).toBe(
      "# Responsibilities\n\n- Build APIs\n- Improve reliability",
    );
    expect(
      screen.queryByRole("heading", { name: "Responsibilities" }),
    ).not.toBeInTheDocument();
  });
});
