import { act, fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ChatInput } from "./ChatInput";

vi.mock("../api/client", () => ({
	api: {
		listProjectFiles: vi.fn().mockResolvedValue([]),
	},
}));

const defaultProps = {
	loading: false,
	queuedMessages: [],
	onSubmit: vi.fn(),
	onCancel: vi.fn(),
	onRemoveQueuedMessage: vi.fn(),
};

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

describe("Slash command picker overlay (Story 438 AC1)", () => {
	it("shows slash command picker when / is typed at position 0", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		expect(screen.getByTestId("slash-command-picker")).toBeInTheDocument();
	});

	it("does not show slash command picker for plain text", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "hello" } });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
	});

	it("does not show slash command picker when / is not at position 0", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "hello /world" } });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
	});
});

describe("Slash command list (Story 438 AC2)", () => {
	it("lists slash commands with name and description", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		expect(screen.getByTestId("slash-command-picker")).toBeInTheDocument();
		// First command should be /help
		expect(screen.getByTestId("slash-command-item-0")).toBeInTheDocument();
		expect(screen.getByTestId("slash-command-item-0")).toHaveTextContent(
			"/help",
		);
	});
});

describe("Slash command fuzzy filter (Story 438 AC3)", () => {
	it("filters commands when typing after /", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/hel" } });
		});

		expect(screen.getByTestId("slash-command-picker")).toBeInTheDocument();
		// /help should match "hel"
		expect(screen.getByTestId("slash-command-item-0")).toHaveTextContent(
			"/help",
		);
		// /rebuild should not be visible (no match for "hel")
		const items = screen.queryAllByTestId(/^slash-command-item-/);
		const texts = items.map((el) => el.textContent ?? "");
		expect(texts.some((t) => t.includes("/rebuild"))).toBe(false);
	});

	it("shows no picker when query matches nothing", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/zzzzz" } });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
	});
});

describe("Slash command keyboard navigation (Story 438 AC4)", () => {
	it("ArrowDown navigates to next item", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		const item0 = screen.getByTestId("slash-command-item-0");
		expect(item0).toHaveStyle({ background: "#2d4a6e" });

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "ArrowDown" });
		});

		const item1 = screen.getByTestId("slash-command-item-1");
		expect(item1).toHaveStyle({ background: "#2d4a6e" });
	});

	it("ArrowUp stays at 0 when already at top", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "ArrowUp" });
		});

		const item0 = screen.getByTestId("slash-command-item-0");
		expect(item0).toHaveStyle({ background: "#2d4a6e" });
	});

	it("Enter selects the highlighted command and inserts it", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/hel" } });
		});

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Enter" });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
		expect((textarea as HTMLTextAreaElement).value).toBe("/help ");
	});

	it("Tab selects the highlighted command and inserts it", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/hel" } });
		});

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Tab" });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
		expect((textarea as HTMLTextAreaElement).value).toBe("/help ");
	});

	it("Escape dismisses the picker", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		expect(screen.getByTestId("slash-command-picker")).toBeInTheDocument();

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Escape" });
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
	});
});

describe("Slash command selection inserts with trailing space (Story 438 AC5)", () => {
	it("clicking a command inserts /<command> with trailing space", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/" } });
		});

		await act(async () => {
			fireEvent.click(screen.getByTestId("slash-command-item-0"));
		});

		expect(
			screen.queryByTestId("slash-command-picker"),
		).not.toBeInTheDocument();
		const val = (textarea as HTMLTextAreaElement).value;
		expect(val).toMatch(/^\/\w+ $/);
	});

	it("selection inserts only the base command (no argument placeholders)", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "/ass" } });
		});

		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Enter" });
		});

		expect((textarea as HTMLTextAreaElement).value).toBe("/assign ");
	});
});
