import { act, render, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { AgentEvent, AgentInfo } from "../api/agents";

vi.mock("../api/client", async () => {
	const actual =
		await vi.importActual<typeof import("../api/client")>("../api/client");
	return {
		...actual,
		api: {
			...actual.api,
			getWorkItemContent: vi.fn(),
			getTestResults: vi.fn(),
			getTokenCost: vi.fn(),
		},
	};
});

vi.mock("../api/agents", () => ({
	agentsApi: {
		listAgents: vi.fn(),
		getAgentConfig: vi.fn(),
		stopAgent: vi.fn(),
		startAgent: vi.fn(),
	},
	subscribeAgentStream: vi.fn(() => () => {}),
}));

import { agentsApi, subscribeAgentStream } from "../api/agents";
import { api } from "../api/client";

const { WorkItemDetailPanel } = await import("./WorkItemDetailPanel");

const mockedGetWorkItemContent = vi.mocked(api.getWorkItemContent);
const mockedGetTestResults = vi.mocked(api.getTestResults);
const mockedGetTokenCost = vi.mocked(api.getTokenCost);
const mockedListAgents = vi.mocked(agentsApi.listAgents);
const mockedGetAgentConfig = vi.mocked(agentsApi.getAgentConfig);
const mockedSubscribeAgentStream = vi.mocked(subscribeAgentStream);

const DEFAULT_CONTENT = {
	content: "# Big Title\n\nSome content here.",
	stage: "current",
	name: "Big Title Story",
	agent: null,
	origin: null,
};

beforeEach(() => {
	vi.clearAllMocks();
	mockedGetWorkItemContent.mockResolvedValue(DEFAULT_CONTENT);
	mockedGetTestResults.mockResolvedValue(null);
	mockedGetTokenCost.mockResolvedValue({ total_cost_usd: 0, agents: [] });
	mockedListAgents.mockResolvedValue([]);
	mockedGetAgentConfig.mockResolvedValue([]);
	mockedSubscribeAgentStream.mockReturnValue(() => {});
});

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

describe("WorkItemDetailPanel - Agent Logs", () => {
	it("shows placeholder when no agent is assigned to the story", async () => {
		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);
		await screen.findByTestId("detail-panel-content");
		const placeholder = screen.getByTestId("placeholder-agent-logs");
		expect(placeholder).toBeInTheDocument();
		expect(placeholder).toHaveTextContent("Coming soon");
	});

	it("shows agent name and running status when agent is running", async () => {
		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		const statusBadge = await screen.findByTestId("agent-status-badge");
		expect(statusBadge).toHaveTextContent("coder-1");
		expect(statusBadge).toHaveTextContent("running");
	});

	it("shows log output when agent emits output events", async () => {
		let emitEvent: ((e: AgentEvent) => void) | null = null;
		mockedSubscribeAgentStream.mockImplementation(
			(_storyId, _agentName, onEvent) => {
				emitEvent = onEvent;
				return () => {};
			},
		);

		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		await screen.findByTestId("agent-status-badge");

		await act(async () => {
			emitEvent?.({
				type: "output",
				story_id: "42_story_test",
				agent_name: "coder-1",
				text: "Writing tests...",
			});
		});

		const logOutput = screen.getByTestId("agent-log-output");
		expect(logOutput).toHaveTextContent("Writing tests...");
	});

	it("appends multiple output events to the log", async () => {
		let emitEvent: ((e: AgentEvent) => void) | null = null;
		mockedSubscribeAgentStream.mockImplementation(
			(_storyId, _agentName, onEvent) => {
				emitEvent = onEvent;
				return () => {};
			},
		);

		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		await screen.findByTestId("agent-status-badge");

		await act(async () => {
			emitEvent?.({
				type: "output",
				story_id: "42_story_test",
				agent_name: "coder-1",
				text: "Line one\n",
			});
		});

		await act(async () => {
			emitEvent?.({
				type: "output",
				story_id: "42_story_test",
				agent_name: "coder-1",
				text: "Line two\n",
			});
		});

		const logOutput = screen.getByTestId("agent-log-output");
		expect(logOutput.textContent).toContain("Line one");
		expect(logOutput.textContent).toContain("Line two");
	});

	it("updates status to completed after done event", async () => {
		let emitEvent: ((e: AgentEvent) => void) | null = null;
		mockedSubscribeAgentStream.mockImplementation(
			(_storyId, _agentName, onEvent) => {
				emitEvent = onEvent;
				return () => {};
			},
		);

		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		await screen.findByTestId("agent-status-badge");

		await act(async () => {
			emitEvent?.({
				type: "done",
				story_id: "42_story_test",
				agent_name: "coder-1",
				session_id: "session-123",
			});
		});

		const statusBadge = screen.getByTestId("agent-status-badge");
		expect(statusBadge).toHaveTextContent("completed");
	});

	it("shows failed status after error event", async () => {
		let emitEvent: ((e: AgentEvent) => void) | null = null;
		mockedSubscribeAgentStream.mockImplementation(
			(_storyId, _agentName, onEvent) => {
				emitEvent = onEvent;
				return () => {};
			},
		);

		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		await screen.findByTestId("agent-status-badge");

		await act(async () => {
			emitEvent?.({
				type: "error",
				story_id: "42_story_test",
				agent_name: "coder-1",
				message: "Process failed",
			});
		});

		const statusBadge = screen.getByTestId("agent-status-badge");
		expect(statusBadge).toHaveTextContent("failed");

		const logOutput = screen.getByTestId("agent-log-output");
		expect(logOutput.textContent).toContain("[ERROR] Process failed");
	});

	it("shows completed agent status without subscribing to stream", async () => {
		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "completed",
				session_id: "session-123",
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		const statusBadge = await screen.findByTestId("agent-status-badge");
		expect(statusBadge).toHaveTextContent("completed");
		expect(mockedSubscribeAgentStream).not.toHaveBeenCalled();
	});

	it("shows failed agent status for a failed agent without subscribing to stream", async () => {
		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "failed",
				session_id: null,
				worktree_path: null,
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		const statusBadge = await screen.findByTestId("agent-status-badge");
		expect(statusBadge).toHaveTextContent("failed");
		expect(mockedSubscribeAgentStream).not.toHaveBeenCalled();
	});

	it("shows agent logs section (not placeholder) when agent is assigned", async () => {
		const agentList: AgentInfo[] = [
			{
				story_id: "42_story_test",
				agent_name: "coder-1",
				status: "running",
				session_id: null,
				worktree_path: "/tmp/wt",
				base_branch: "master",
				log_session_id: null,
			},
		];
		mockedListAgents.mockResolvedValue(agentList);

		render(
			<WorkItemDetailPanel
				storyId="42_story_test"
				pipelineVersion={0}
				onClose={() => {}}
			/>,
		);

		await screen.findByTestId("agent-logs-section");

		expect(
			screen.queryByTestId("placeholder-agent-logs"),
		).not.toBeInTheDocument();
	});
});
