/**
 * FocusView tests -- the main focus container.
 *
 * Tests:
 * 1. Shows FocusCard when focus is set
 * 2. Shows empty state when no focus and no items
 * 3. Shows suggestion when no focus but items exist
 * 4. Auto-advances to next item after start
 * 5. Shows celebration on last item complete
 * 6. Skip/Defer/Complete trigger ReasonPrompt before action
 * 7. ReasonPrompt can be cancelled with Escape
 * 8. Confirm with reason logs decision via useActions
 */

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { FocusView } from "@/components/FocusView";
import { useFocusStore } from "@/stores/focus-store";
import { makeFocusItem } from "../helpers/fixtures";

// Mock the shell plugin for URL opening
vi.mock("@tauri-apps/plugin-shell", () => ({
  open: vi.fn(() => Promise.resolve()),
}));

// Mock Tauri invoke -- useActions calls invoke for log_decision, update_item, close_bead
const mockInvoke = vi.fn(() => Promise.resolve());
vi.mock("@tauri-apps/api/core", () => ({
  invoke: (...args: unknown[]) => mockInvoke(...args),
}));

function renderWithProviders(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  });
  return render(
    <QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
  );
}

describe("FocusView", () => {
  beforeEach(() => {
    localStorage.clear();
    useFocusStore.setState({
      current: null,
      queue: [],
      isLoading: false,
      error: null,
    });
    mockInvoke.mockResolvedValue(undefined);
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  describe("with focus set", () => {
    it("shows FocusCard when focus is set", () => {
      const item = makeFocusItem({ id: "1", title: "Test Item" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      expect(screen.getByText("Test Item")).toBeInTheDocument();
      expect(screen.getByRole("button", { name: /start/i })).toBeInTheDocument();
    });

    it("shows queue summary when items exist in queue", () => {
      const current = makeFocusItem({ id: "1", title: "Current" });
      const queued = makeFocusItem({ id: "2", title: "Queued", type: "issue" });
      useFocusStore.setState({ current, queue: [queued] });

      renderWithProviders(<FocusView />);

      expect(screen.getByText(/Queue:/)).toBeInTheDocument();
      expect(screen.getByText(/1 issue/)).toBeInTheDocument();
    });
  });

  describe("empty state", () => {
    it("shows empty state when no focus and no items", () => {
      useFocusStore.setState({ current: null, queue: [] });

      renderWithProviders(<FocusView />);

      expect(screen.getByText(/all clear/i)).toBeInTheDocument();
      expect(screen.getByText(/nothing needs your attention/i)).toBeInTheDocument();
    });

    it("shows celebration message in empty state", () => {
      useFocusStore.setState({ current: null, queue: [] });

      renderWithProviders(<FocusView />);

      expect(screen.getByText(/nice work/i)).toBeInTheDocument();
    });
  });

  describe("suggestion state", () => {
    it("shows suggestion when no focus but items exist in queue", () => {
      const item = makeFocusItem({ id: "1", title: "Suggested Item" });
      useFocusStore.setState({ current: null, queue: [item] });

      renderWithProviders(<FocusView />);

      // Should show the item as a suggestion
      expect(screen.getByText("Suggested Item")).toBeInTheDocument();
      // Should have a "Set as focus" or "Start" button
      expect(
        screen.getByRole("button", { name: /set as focus|start/i })
      ).toBeInTheDocument();
    });

    it("promotes suggestion to focus when user clicks set as focus", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Suggested Item" });
      useFocusStore.setState({ current: null, queue: [item] });

      renderWithProviders(<FocusView />);

      // Click the set as focus button
      await user.click(screen.getByRole("button", { name: /set as focus|start/i }));

      // Item should now be the current focus
      expect(useFocusStore.getState().current?.id).toBe("1");
    });
  });

  describe("auto-advance behavior", () => {
    it("auto-advances to next item after start", async () => {
      const user = userEvent.setup();
      const item1 = makeFocusItem({ id: "1", title: "First Item" });
      const item2 = makeFocusItem({ id: "2", title: "Second Item" });
      useFocusStore.setState({ current: item1, queue: [item2] });

      renderWithProviders(<FocusView />);

      // Start does not trigger ReasonPrompt -- it goes straight through
      await user.click(screen.getByRole("button", { name: /start/i }));

      // Should log decision via invoke
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "log_decision",
          expect.objectContaining({
            entry: expect.objectContaining({
              action: "start",
              bead_id: "1",
            }),
          })
        );
      });
    });

    it("shows empty state after last item start", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Only Item" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      // Start the only item (no ReasonPrompt for start)
      await user.click(screen.getByRole("button", { name: /start/i }));

      // log_decision is called asynchronously; start doesn't advance the queue
      // (start opens URL + logs, but doesn't call act to advance)
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "log_decision",
          expect.anything()
        );
      });
    });
  });

  describe("focus selection", () => {
    it("allows selecting a specific item as focus via setFocus", () => {
      const item1 = makeFocusItem({ id: "1", title: "First" });
      const item2 = makeFocusItem({ id: "2", title: "Second" });
      const item3 = makeFocusItem({ id: "3", title: "Third" });
      useFocusStore.setState({ current: item1, queue: [item2, item3] });

      // Use setFocus to promote item3
      useFocusStore.getState().setFocus("3");

      const state = useFocusStore.getState();
      expect(state.current?.id).toBe("3");
      expect(state.queue.map((i) => i.id)).toContain("1");
      expect(state.queue.map((i) => i.id)).toContain("2");
    });
  });

  describe("loading and error states", () => {
    it("shows loading state", () => {
      useFocusStore.setState({ isLoading: true });

      renderWithProviders(<FocusView />);

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

    it("shows error state", () => {
      useFocusStore.setState({ error: "Something went wrong" });

      renderWithProviders(<FocusView />);

      expect(screen.getByText("Something went wrong")).toBeInTheDocument();
    });
  });

  describe("ReasonPrompt wiring", () => {
    it("AC-F2.1: Skip action triggers ReasonPrompt with 'Skipping: [title]'", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Fix auth bug" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);
      // Button accessible name includes shortcut text: "SkipCmd+S"
      await user.click(screen.getByRole("button", { name: /^Skip/i }));

      // ReasonPrompt should appear with the action and title
      expect(screen.getByRole("dialog")).toBeInTheDocument();
      expect(screen.getByText(/skipping/i)).toBeInTheDocument();
      // Title appears in both FocusCard and ReasonPrompt -- check within dialog
      const dialog = screen.getByRole("dialog");
      expect(dialog).toHaveTextContent("Fix auth bug");
    });

    it("AC-F2.5: 'Skip reason' proceeds with reason=null", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Fix auth bug" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      // Trigger skip to open ReasonPrompt (button name includes shortcut)
      await user.click(screen.getByRole("button", { name: /^Skip/ }));

      // Click "Skip reason" button in the prompt
      await user.click(screen.getByRole("button", { name: /skip reason/i }));

      // Should have called update_item and log_decision via useActions
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "log_decision",
          expect.objectContaining({
            entry: expect.objectContaining({
              action: "skip",
              bead_id: "1",
              reason: null,
            }),
          })
        );
      });

      // ReasonPrompt should be gone
      expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
    });

    it("AC-F2.7: Escape cancels prompt", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Fix auth bug" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      // Trigger skip to open ReasonPrompt (button name includes shortcut)
      await user.click(screen.getByRole("button", { name: /^Skip/ }));
      expect(screen.getByRole("dialog")).toBeInTheDocument();

      // Press Escape
      await user.keyboard("{Escape}");

      // ReasonPrompt should be dismissed
      expect(screen.queryByRole("dialog")).not.toBeInTheDocument();

      // No backend calls should have been made
      expect(mockInvoke).not.toHaveBeenCalled();
    });

    it("AC-F2.8: Confirm logs decision with reason and tags", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Fix auth bug" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      // Trigger skip to open ReasonPrompt (button name includes shortcut)
      await user.click(screen.getByRole("button", { name: /^Skip/ }));

      // Type a reason
      const textarea = screen.getByRole("textbox");
      await user.type(textarea, "Need more context from Sarah");

      // Select a tag
      await user.click(screen.getByRole("button", { name: /blocking/i }));

      // Click Confirm
      await user.click(screen.getByRole("button", { name: /confirm/i }));

      // Should have logged with reason and tags
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "log_decision",
          expect.objectContaining({
            entry: expect.objectContaining({
              action: "skip",
              bead_id: "1",
              reason: "Need more context from Sarah",
              tags: ["blocking"],
            }),
          })
        );
      });
    });

    it("Defer 1h triggers ReasonPrompt then calls defer action", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Review MR" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);

      // Trigger defer 1h
      await user.click(screen.getByRole("button", { name: /1 hour/i }));

      // ReasonPrompt should appear with defer action
      expect(screen.getByRole("dialog")).toBeInTheDocument();
      expect(screen.getByText(/deferring/i)).toBeInTheDocument();

      // Skip reason to confirm quickly
      await user.click(screen.getByRole("button", { name: /skip reason/i }));

      // Should have called update_item with snooze time
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "update_item",
          expect.objectContaining({
            id: "1",
            updates: expect.objectContaining({
              snoozed_until: expect.any(String),
            }),
          })
        );
      });
    });

    it("Start does NOT trigger ReasonPrompt", async () => {
      const user = userEvent.setup();
      const item = makeFocusItem({ id: "1", title: "Test" });
      useFocusStore.setState({ current: item, queue: [] });

      renderWithProviders(<FocusView />);
      await user.click(screen.getByRole("button", { name: /start/i }));

      // No dialog should appear
      expect(screen.queryByRole("dialog")).not.toBeInTheDocument();

      // But log_decision should be called directly
      await waitFor(() => {
        expect(mockInvoke).toHaveBeenCalledWith(
          "log_decision",
          expect.objectContaining({
            entry: expect.objectContaining({
              action: "start",
            }),
          })
        );
      });
    });
  });
});
