// npx vitest src/components/modes/__tests__/ModesView.spec.tsx

import { render, screen, fireEvent, waitFor } from "@/utils/test-utils"
import ModesView from "../ModesView"
import { ExtensionStateContext } from "@src/context/ExtensionStateContext"
import { vscode } from "@src/utils/vscode"

// Mock vscode API
vitest.mock("@src/utils/vscode", () => ({
	vscode: {
		postMessage: vitest.fn(),
	},
}))

const mockExtensionState = {
	customModePrompts: {},
	listApiConfigMeta: [
		{ id: "config1", name: "Config 1" },
		{ id: "config2", name: "Config 2" },
	],
	enhancementApiConfigId: "",
	setEnhancementApiConfigId: vitest.fn(),
	mode: "code",
	customModes: [],
	customSupportPrompts: [],
	currentApiConfigName: "",
	customInstructions: "Initial instructions",
	setCustomInstructions: vitest.fn(),
}

const renderPromptsView = (props = {}) => {
	const mockOnDone = vitest.fn()
	return render(
		<ExtensionStateContext.Provider value={{ ...mockExtensionState, ...props } as any}>
			<ModesView onDone={mockOnDone} />
		</ExtensionStateContext.Provider>,
	)
}

Element.prototype.scrollIntoView = vitest.fn()

describe("PromptsView", () => {
	beforeEach(() => {
		vitest.clearAllMocks()
	})

	it("displays the current mode name in the select trigger", () => {
		renderPromptsView({ mode: "code" })
		const selectTrigger = screen.getByTestId("mode-select-trigger")
		expect(selectTrigger).toHaveTextContent("Code")
	})

	it("opens the mode selection popover when the trigger is clicked", async () => {
		renderPromptsView()
		const selectTrigger = screen.getByTestId("mode-select-trigger")
		fireEvent.click(selectTrigger)
		await waitFor(() => {
			expect(selectTrigger).toHaveAttribute("aria-expanded", "true")
		})
	})

	it("filters mode options based on search input", async () => {
		renderPromptsView()
		const selectTrigger = screen.getByTestId("mode-select-trigger")
		fireEvent.click(selectTrigger)

		const searchInput = screen.getByTestId("mode-search-input")
		fireEvent.change(searchInput, { target: { value: "ask" } })

		await waitFor(() => {
			expect(screen.getByTestId("mode-option-ask")).toBeInTheDocument()
			expect(screen.queryByTestId("mode-option-code")).not.toBeInTheDocument()
			expect(screen.queryByTestId("mode-option-architect")).not.toBeInTheDocument()
		})
	})

	it("selects a mode from the dropdown and sends update message", async () => {
		renderPromptsView()
		const selectTrigger = screen.getByTestId("mode-select-trigger")
		fireEvent.click(selectTrigger)

		const askOption = await waitFor(() => screen.getByTestId("mode-option-ask"))
		fireEvent.click(askOption)

		expect(mockExtensionState.setEnhancementApiConfigId).not.toHaveBeenCalled() // Ensure this is not called by mode switch
		expect(vscode.postMessage).toHaveBeenCalledWith({
			type: "mode",
			text: "ask",
		})
		await waitFor(() => {
			expect(selectTrigger).toHaveAttribute("aria-expanded", "false")
		})
	})

	it("handles prompt changes correctly", async () => {
		renderPromptsView()

		// Get the textarea
		const textarea = await waitFor(() => screen.getByTestId("code-prompt-textarea"))

		// Simulate VSCode TextArea change event
		const changeEvent = new CustomEvent("change", {
			detail: {
				target: {
					value: "New prompt value",
				},
			},
		})

		fireEvent(textarea, changeEvent)

		expect(vscode.postMessage).toHaveBeenCalledWith({
			type: "updatePrompt",
			promptMode: "code",
			customPrompt: { roleDefinition: "New prompt value" },
		})
	})

	it("resets role definition only for built-in modes", async () => {
		const customMode = {
			slug: "custom-mode",
			name: "Custom Mode",
			roleDefinition: "Custom role",
			groups: [],
		}

		// Test with built-in mode (code)
		const { unmount } = render(
			<ExtensionStateContext.Provider
				value={{ ...mockExtensionState, mode: "code", customModes: [customMode] } as any}>
				<ModesView onDone={vitest.fn()} />
			</ExtensionStateContext.Provider>,
		)

		// Find and click the role definition reset button
		const resetButton = screen.getByTestId("role-definition-reset")
		expect(resetButton).toBeInTheDocument()
		await fireEvent.click(resetButton)

		// Verify it only resets role definition
		// When resetting a built-in mode's role definition, the field should be removed entirely
		// from the customPrompt object, not set to undefined.
		// This allows the default role definition from the built-in mode to be used instead.
		expect(vscode.postMessage).toHaveBeenCalledWith({
			type: "updatePrompt",
			promptMode: "code",
			customPrompt: {}, // Empty object because the role definition field is removed entirely
		})

		// Cleanup before testing custom mode
		unmount()

		// Test with custom mode
		render(
			<ExtensionStateContext.Provider
				value={{ ...mockExtensionState, mode: "custom-mode", customModes: [customMode] } as any}>
				<ModesView onDone={vitest.fn()} />
			</ExtensionStateContext.Provider>,
		)

		// Verify reset button is not present for custom mode
		expect(screen.queryByTestId("role-definition-reset")).not.toBeInTheDocument()
	})

	it("description section behavior for different mode types", async () => {
		const customMode = {
			slug: "custom-mode",
			name: "Custom Mode",
			roleDefinition: "Custom role",
			description: "Custom description",
			groups: [],
		}

		// Test with built-in mode (code) - description section should be shown with reset button
		const { unmount } = render(
			<ExtensionStateContext.Provider
				value={{ ...mockExtensionState, mode: "code", customModes: [customMode] } as any}>
				<ModesView onDone={vitest.fn()} />
			</ExtensionStateContext.Provider>,
		)

		// Verify description reset button IS present for built-in modes
		// because built-in modes can have their descriptions customized and reset
		expect(screen.queryByTestId("description-reset")).toBeInTheDocument()

		// Cleanup before testing custom mode
		unmount()

		// Test with custom mode - description section should be shown
		render(
			<ExtensionStateContext.Provider
				value={{ ...mockExtensionState, mode: "custom-mode", customModes: [customMode] } as any}>
				<ModesView onDone={vitest.fn()} />
			</ExtensionStateContext.Provider>,
		)

		// Verify description section is present for custom modes
		// but reset button is NOT present (since custom modes manage their own descriptions)
		expect(screen.queryByTestId("description-reset")).not.toBeInTheDocument()

		// Verify the description text field is present for custom modes
		expect(screen.getByTestId("custom-mode-description-textfield")).toBeInTheDocument()
	})

	it("handles clearing custom instructions correctly", async () => {
		const setCustomInstructions = vitest.fn()
		renderPromptsView({
			...mockExtensionState,
			customInstructions: "Initial instructions",
			setCustomInstructions,
		})

		const textarea = screen.getByTestId("global-custom-instructions-textarea")

		// Simulate VSCode TextArea change event with empty value
		// We need to simulate both the CustomEvent format and regular event format
		// since the component handles both
		Object.defineProperty(textarea, "value", {
			writable: true,
			value: "",
		})

		const changeEvent = new Event("change", { bubbles: true })
		fireEvent(textarea, changeEvent)

		// The component calls setCustomInstructions with value ?? undefined
		// With nullish coalescing, empty string is preserved (not treated as nullish)
		expect(setCustomInstructions).toHaveBeenCalledWith("")
		// The postMessage call will have multiple calls, we need to check the right one
		expect(vscode.postMessage).toHaveBeenCalledWith({
			type: "customInstructions",
			text: "", // empty string is now preserved with ?? operator
		})
	})

	it("closes the mode selection popover when ESC key is pressed", async () => {
		renderPromptsView()
		const selectTrigger = screen.getByTestId("mode-select-trigger")

		// Open the popover
		fireEvent.click(selectTrigger)
		await waitFor(() => {
			expect(selectTrigger).toHaveAttribute("aria-expanded", "true")
		})

		// Press ESC key
		fireEvent.keyDown(window, { key: "Escape" })

		// Verify popover is closed
		await waitFor(() => {
			expect(selectTrigger).toHaveAttribute("aria-expanded", "false")
		})
	})

	it("does not close the popover when ESC is pressed while popover is closed", async () => {
		renderPromptsView()
		const selectTrigger = screen.getByTestId("mode-select-trigger")

		// Ensure popover is closed
		expect(selectTrigger).toHaveAttribute("aria-expanded", "false")

		// Press ESC key
		fireEvent.keyDown(window, { key: "Escape" })

		// Verify popover remains closed
		expect(selectTrigger).toHaveAttribute("aria-expanded", "false")
	})
})
