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

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

const mockedListProjectFiles = vi.mocked(api.listProjectFiles);

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

beforeEach(() => {
	vi.clearAllMocks();
	mockedListProjectFiles.mockResolvedValue([
		"src/main.rs",
		"src/lib.rs",
		"frontend/index.html",
		"README.md",
	]);
});

describe("File picker overlay (Story 269 AC1)", () => {
	it("shows file picker overlay when @ is typed", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});
	});

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

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

		expect(screen.queryByTestId("file-picker-overlay")).not.toBeInTheDocument();
	});
});

describe("File picker fuzzy matching (Story 269 AC2)", () => {
	it("filters files by query typed after @", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});

		// main.rs should be visible, README.md should not
		expect(screen.getByText("src/main.rs")).toBeInTheDocument();
		expect(screen.queryByText("README.md")).not.toBeInTheDocument();
	});

	it("shows all files when @ is typed with no query", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});

		// All 4 files should be visible
		expect(screen.getByText("src/main.rs")).toBeInTheDocument();
		expect(screen.getByText("src/lib.rs")).toBeInTheDocument();
		expect(screen.getByText("README.md")).toBeInTheDocument();
	});
});

describe("File picker selection (Story 269 AC3)", () => {
	it("clicking a file inserts @path into the message", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-item-0")).toBeInTheDocument();
		});

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

		// Picker should be dismissed and the file reference inserted
		expect(screen.queryByTestId("file-picker-overlay")).not.toBeInTheDocument();
		expect((textarea as HTMLTextAreaElement).value).toMatch(/^@\S+/);
	});

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

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});

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

		expect(screen.queryByTestId("file-picker-overlay")).not.toBeInTheDocument();
		expect((textarea as HTMLTextAreaElement).value).toContain("@src/main.rs");
	});
});

describe("File picker dismiss (Story 269 AC5)", () => {
	it("Escape key dismisses the file picker", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});

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

		expect(screen.queryByTestId("file-picker-overlay")).not.toBeInTheDocument();
	});
});

describe("Multiple @ references (Story 269 AC6)", () => {
	it("typing @ after a completed reference triggers picker again", async () => {
		render(<ChatInput {...defaultProps} />);
		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});

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

		// Type a second @
		await act(async () => {
			const current = (textarea as HTMLTextAreaElement).value;
			fireEvent.change(textarea, { target: { value: `${current} @` } });
		});

		await waitFor(() => {
			expect(screen.getByTestId("file-picker-overlay")).toBeInTheDocument();
		});
	});
});
