import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { QueueView } from "@/components/QueueView";
import { useFocusStore } from "@/stores/focus-store";
import { useBatchStore } from "@/stores/batch-store";
import { makeFocusItem } from "../helpers/fixtures";

describe("QueueView", () => {
  beforeEach(() => {
    useFocusStore.setState({
      current: null,
      queue: [],
      isLoading: false,
      error: null,
    });
    useBatchStore.setState({
      isActive: false,
      batchLabel: "",
      items: [],
      statuses: [],
      currentIndex: 0,
      startedAt: null,
    });
  });

  it("shows empty state when no items", () => {
    render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);
    expect(screen.getByText(/no items/i)).toBeInTheDocument();
  });

  it("groups items by type with section headers", () => {
    useFocusStore.setState({
      current: makeFocusItem({ id: "current" }),
      queue: [
        makeFocusItem({ id: "r1", type: "mr_review", title: "Review A" }),
        makeFocusItem({ id: "r2", type: "mr_review", title: "Review B" }),
        makeFocusItem({ id: "i1", type: "issue", title: "Issue A" }),
        makeFocusItem({
          id: "m1",
          type: "mr_authored",
          title: "My MR",
        }),
      ],
    });

    render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

    expect(screen.getByText(/REVIEWS/i)).toBeInTheDocument();
    expect(screen.getByText(/ISSUES/i)).toBeInTheDocument();
    expect(screen.getByText(/AUTHORED MRS/i)).toBeInTheDocument();
  });

  it("shows item count in section headers", () => {
    useFocusStore.setState({
      current: makeFocusItem({ id: "current", type: "issue" }),
      queue: [
        makeFocusItem({ id: "r1", type: "mr_review" }),
        makeFocusItem({ id: "r2", type: "mr_review" }),
        makeFocusItem({ id: "i1", type: "issue" }),
      ],
    });

    render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

    // Text is split across elements, so use a function matcher
    const reviewsHeader = screen.getByText((_content, element) => {
      return element?.tagName === "H2" && element.textContent === "REVIEWS (2)";
    });
    expect(reviewsHeader).toBeInTheDocument();

    const issuesHeader = screen.getByText((_content, element) => {
      return element?.tagName === "H2" && element.textContent === "ISSUES (2)";
    });
    expect(issuesHeader).toBeInTheDocument();
  });

  it("includes current focus item in the list", () => {
    useFocusStore.setState({
      current: makeFocusItem({
        id: "focused",
        type: "mr_review",
        title: "Focused item",
      }),
      queue: [
        makeFocusItem({ id: "q1", type: "issue", title: "Queued item" }),
      ],
    });

    render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

    expect(screen.getByText("Focused item")).toBeInTheDocument();
    expect(screen.getByText("Queued item")).toBeInTheDocument();
  });

  it("calls onSetFocus and switches to focus when an item is clicked", async () => {
    const onSetFocus = vi.fn();
    const onSwitchToFocus = vi.fn();
    const user = userEvent.setup();

    useFocusStore.setState({
      current: makeFocusItem({ id: "current" }),
      queue: [
        makeFocusItem({ id: "target", type: "issue", title: "Click me" }),
      ],
    });

    render(
      <QueueView onSetFocus={onSetFocus} onSwitchToFocus={onSwitchToFocus} />
    );

    await user.click(screen.getByText("Click me"));
    expect(onSetFocus).toHaveBeenCalledWith("target");
    expect(onSwitchToFocus).toHaveBeenCalled();
  });

  it("marks the current focus item visually", () => {
    useFocusStore.setState({
      current: makeFocusItem({ id: "focused", title: "Current focus" }),
      queue: [],
    });

    const { container } = render(
      <QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />
    );

    expect(container.querySelector("[data-focused='true']")).toBeTruthy();
  });

  // -- Snoozed items filtering --

  describe("snoozed items", () => {
    it("hides snoozed items by default", () => {
      const future = new Date(Date.now() + 60 * 60 * 1000).toISOString();

      useFocusStore.setState({
        current: makeFocusItem({ id: "active", title: "Active item" }),
        queue: [
          makeFocusItem({
            id: "snoozed",
            type: "issue",
            title: "Snoozed item",
            snoozedUntil: future,
          }),
          makeFocusItem({
            id: "visible",
            type: "issue",
            title: "Visible item",
            snoozedUntil: null,
          }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      expect(screen.queryByText("Snoozed item")).not.toBeInTheDocument();
      expect(screen.getByText("Visible item")).toBeInTheDocument();
    });

    it("shows snoozed items when showSnoozed is true", () => {
      const future = new Date(Date.now() + 60 * 60 * 1000).toISOString();

      useFocusStore.setState({
        current: makeFocusItem({ id: "active", title: "Active item" }),
        queue: [
          makeFocusItem({
            id: "snoozed",
            type: "issue",
            title: "Snoozed item",
            snoozedUntil: future,
          }),
        ],
      });

      render(
        <QueueView
          onSetFocus={vi.fn()}
          onSwitchToFocus={vi.fn()}
          showSnoozed={true}
        />
      );

      expect(screen.getByText("Snoozed item")).toBeInTheDocument();
    });

    it("shows items with expired snooze time", () => {
      const past = new Date(Date.now() - 60 * 60 * 1000).toISOString();

      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({
            id: "expired",
            type: "issue",
            title: "Expired snooze",
            snoozedUntil: past,
          }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      expect(screen.getByText("Expired snooze")).toBeInTheDocument();
    });

    it("shows snooze count indicator when items are hidden", () => {
      const future = new Date(Date.now() + 60 * 60 * 1000).toISOString();

      useFocusStore.setState({
        current: makeFocusItem({ id: "active", title: "Active item" }),
        queue: [
          makeFocusItem({
            id: "snoozed1",
            type: "issue",
            title: "Snoozed 1",
            snoozedUntil: future,
          }),
          makeFocusItem({
            id: "snoozed2",
            type: "mr_review",
            title: "Snoozed 2",
            snoozedUntil: future,
          }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      expect(screen.getByText(/2 snoozed/i)).toBeInTheDocument();
    });
  });

  // -- Filtering via type --

  describe("filtering", () => {
    it("filters items by type when filter is applied", () => {
      useFocusStore.setState({
        current: makeFocusItem({ id: "current", type: "mr_review" }),
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "i1", type: "issue", title: "Issue 1" }),
          makeFocusItem({ id: "m1", type: "manual", title: "Task 1" }),
        ],
      });

      render(
        <QueueView
          onSetFocus={vi.fn()}
          onSwitchToFocus={vi.fn()}
          filterType="issue"
        />
      );

      expect(screen.queryByText("Review 1")).not.toBeInTheDocument();
      expect(screen.getByText("Issue 1")).toBeInTheDocument();
      expect(screen.queryByText("Task 1")).not.toBeInTheDocument();
    });

    it("shows all types when no filter is applied", () => {
      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "i1", type: "issue", title: "Issue 1" }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      expect(screen.getByText("Review 1")).toBeInTheDocument();
      expect(screen.getByText("Issue 1")).toBeInTheDocument();
    });
  });

  // -- Batch mode entry --

  describe("batch mode", () => {
    it("shows batch button for sections with multiple items", () => {
      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "r2", type: "mr_review", title: "Review 2" }),
          makeFocusItem({ id: "r3", type: "mr_review", title: "Review 3" }),
        ],
      });

      render(
        <QueueView
          onSetFocus={vi.fn()}
          onSwitchToFocus={vi.fn()}
          onStartBatch={vi.fn()}
        />
      );

      const batchButton = screen.getByRole("button", { name: /batch/i });
      expect(batchButton).toBeInTheDocument();
    });

    it("does not show batch button for sections with single item", () => {
      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "i1", type: "issue", title: "Issue 1" }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      // Neither section has multiple items
      expect(
        screen.queryByRole("button", { name: /batch/i })
      ).not.toBeInTheDocument();
    });

    it("calls onStartBatch with section items when batch button clicked", async () => {
      const onStartBatch = vi.fn();
      const user = userEvent.setup();

      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "r2", type: "mr_review", title: "Review 2" }),
        ],
      });

      render(
        <QueueView
          onSetFocus={vi.fn()}
          onSwitchToFocus={vi.fn()}
          onStartBatch={onStartBatch}
        />
      );

      const batchButton = screen.getByRole("button", { name: /batch/i });
      await user.click(batchButton);

      expect(onStartBatch).toHaveBeenCalledWith(
        expect.arrayContaining([
          expect.objectContaining({ id: "r1" }),
          expect.objectContaining({ id: "r2" }),
        ]),
        "REVIEWS"
      );
    });
  });

  // -- Command palette integration --

  describe("command palette", () => {
    it("opens command palette on Cmd+K", async () => {
      const user = userEvent.setup();

      useFocusStore.setState({
        current: makeFocusItem({ id: "current" }),
        queue: [],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      await user.keyboard("{Meta>}k{/Meta}");

      expect(screen.getByRole("dialog")).toBeInTheDocument();
    });

    it("filters items when command is selected", async () => {
      const user = userEvent.setup();

      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "r1", type: "mr_review", title: "Review 1" }),
          makeFocusItem({ id: "i1", type: "issue", title: "Issue 1" }),
        ],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      // Open palette
      await user.keyboard("{Meta>}k{/Meta}");

      // Type filter command prefix to enter command mode
      const input = screen.getByRole("textbox");
      await user.type(input, "type:");

      // Click the type:issue option directly
      const issueOption = screen.getByRole("option", { name: /type:issue/i });
      await user.click(issueOption);

      // Should only show issues
      expect(screen.queryByText("Review 1")).not.toBeInTheDocument();
      expect(screen.getByText("Issue 1")).toBeInTheDocument();
    });
  });

  // -- Header actions --

  describe("header", () => {
    it("shows Back to Focus button", () => {
      useFocusStore.setState({
        current: makeFocusItem({ id: "current" }),
        queue: [],
      });

      render(<QueueView onSetFocus={vi.fn()} onSwitchToFocus={vi.fn()} />);

      expect(
        screen.getByRole("button", { name: /back to focus/i })
      ).toBeInTheDocument();
    });

    it("calls onSwitchToFocus when Back to Focus clicked", async () => {
      const onSwitchToFocus = vi.fn();
      const user = userEvent.setup();

      useFocusStore.setState({
        current: makeFocusItem({ id: "current" }),
        queue: [],
      });

      render(
        <QueueView onSetFocus={vi.fn()} onSwitchToFocus={onSwitchToFocus} />
      );

      await user.click(screen.getByRole("button", { name: /back to focus/i }));
      expect(onSwitchToFocus).toHaveBeenCalled();
    });

    it("shows filter indicator when filter is active", () => {
      useFocusStore.setState({
        current: null,
        queue: [
          makeFocusItem({ id: "i1", type: "issue", title: "Issue 1" }),
        ],
      });

      render(
        <QueueView
          onSetFocus={vi.fn()}
          onSwitchToFocus={vi.fn()}
          filterType="issue"
        />
      );

      expect(screen.getByText(/filtered/i)).toBeInTheDocument();
    });
  });
});
