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

describe("ChatInput component (Story 178 AC1)", () => {
	it("renders a textarea with Send a message... placeholder", () => {
		render(
			<ChatInput
				loading={false}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");
		expect(textarea.tagName.toLowerCase()).toBe("textarea");
	});

	it("manages input state internally — typing updates value without calling onSubmit", async () => {
		const onSubmit = vi.fn();

		render(
			<ChatInput
				loading={false}
				queuedMessages={[]}
				onSubmit={onSubmit}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");

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

		expect((textarea as HTMLTextAreaElement).value).toBe("hello world");
		expect(onSubmit).not.toHaveBeenCalled();
	});

	it("calls onSubmit with the input text on Enter key press", async () => {
		const onSubmit = vi.fn();

		render(
			<ChatInput
				loading={false}
				queuedMessages={[]}
				onSubmit={onSubmit}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "test message" } });
		});
		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Enter", shiftKey: false });
		});

		expect(onSubmit).toHaveBeenCalledWith("test message");
	});

	it("clears input after submitting", async () => {
		render(
			<ChatInput
				loading={false}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");

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

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

	it("does not submit on Shift+Enter", async () => {
		const onSubmit = vi.fn();

		render(
			<ChatInput
				loading={false}
				queuedMessages={[]}
				onSubmit={onSubmit}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");

		await act(async () => {
			fireEvent.change(textarea, { target: { value: "multiline" } });
		});
		await act(async () => {
			fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true });
		});

		expect(onSubmit).not.toHaveBeenCalled();
		expect((textarea as HTMLTextAreaElement).value).toBe("multiline");
	});

	it("calls onCancel when stop button is clicked while loading with empty input", async () => {
		const onCancel = vi.fn();

		render(
			<ChatInput
				loading={true}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={onCancel}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const stopButton = screen.getByRole("button", { name: "■" });
		await act(async () => {
			fireEvent.click(stopButton);
		});

		expect(onCancel).toHaveBeenCalled();
	});

	it("renders queued message indicators", () => {
		render(
			<ChatInput
				loading={true}
				queuedMessages={[
					{ id: "1", text: "first message" },
					{ id: "2", text: "second message" },
				]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const indicators = screen.getAllByTestId("queued-message-indicator");
		expect(indicators).toHaveLength(2);
		expect(indicators[0]).toHaveTextContent("first message");
		expect(indicators[1]).toHaveTextContent("second message");
	});

	it("calls onRemoveQueuedMessage when cancel button is clicked", async () => {
		const onRemove = vi.fn();

		render(
			<ChatInput
				loading={true}
				queuedMessages={[{ id: "q1", text: "to remove" }]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={onRemove}
			/>,
		);

		const cancelBtn = screen.getByTitle("Cancel queued message");
		await act(async () => {
			fireEvent.click(cancelBtn);
		});

		expect(onRemove).toHaveBeenCalledWith("q1");
	});

	it("edit button restores queued message text to input and removes from queue", async () => {
		const onRemove = vi.fn();

		render(
			<ChatInput
				loading={true}
				queuedMessages={[{ id: "q1", text: "edit me back" }]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={onRemove}
			/>,
		);

		const editBtn = screen.getByTitle("Edit queued message");
		await act(async () => {
			fireEvent.click(editBtn);
		});

		const textarea = screen.getByPlaceholderText("Send a message...");
		expect((textarea as HTMLTextAreaElement).value).toBe("edit me back");
		expect(onRemove).toHaveBeenCalledWith("q1");
	});
});

describe("ChatInput appendToInput (Bug 215 regression)", () => {
	it("appendToInput sets text into an empty input", async () => {
		const ref = React.createRef<ChatInputHandle>();

		render(
			<ChatInput
				ref={ref}
				loading={false}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		await act(async () => {
			ref.current?.appendToInput("queued message");
		});

		const textarea = screen.getByPlaceholderText("Send a message...");
		expect((textarea as HTMLTextAreaElement).value).toBe("queued message");
	});

	it("appendToInput appends to existing input content with a newline separator", async () => {
		const ref = React.createRef<ChatInputHandle>();

		render(
			<ChatInput
				ref={ref}
				loading={false}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		const textarea = screen.getByPlaceholderText("Send a message...");

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

		await act(async () => {
			ref.current?.appendToInput("appended text");
		});

		expect((textarea as HTMLTextAreaElement).value).toBe(
			"existing text\nappended text",
		);
	});

	it("multiple queued messages joined with newlines are appended on cancel", async () => {
		const ref = React.createRef<ChatInputHandle>();

		render(
			<ChatInput
				ref={ref}
				loading={false}
				queuedMessages={[]}
				onSubmit={vi.fn()}
				onCancel={vi.fn()}
				onRemoveQueuedMessage={vi.fn()}
			/>,
		);

		await act(async () => {
			ref.current?.appendToInput("msg one\nmsg two");
		});

		const textarea = screen.getByPlaceholderText("Send a message...");
		expect((textarea as HTMLTextAreaElement).value).toBe("msg one\nmsg two");
	});
});
