import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import type { PipelineStageItem } from "../api/client";
import { StagePanel } from "./StagePanel";

describe("StagePanel", () => {
	it("renders empty message when no items", () => {
		render(<StagePanel title="Current" items={[]} />);
		expect(screen.getByText("Empty.")).toBeInTheDocument();
	});

	it("renders story item without agent lozenge when agent is null", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "42_story_no_agent",
				name: "No Agent Story",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		expect(screen.getByText("No Agent Story")).toBeInTheDocument();
		// No agent lozenge
		expect(screen.queryByText(/coder-/)).not.toBeInTheDocument();
	});

	it("shows agent lozenge with agent name and model when agent is running", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "43_story_with_agent",
				name: "Active Story",
				error: null,
				merge_failure: null,
				agent: {
					agent_name: "coder-1",
					model: "sonnet",
					status: "running",
				},
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		expect(screen.getByText("Active Story")).toBeInTheDocument();
		expect(screen.getByText("coder-1 sonnet")).toBeInTheDocument();
	});

	it("shows agent lozenge with only agent name when model is null", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "44_story_no_model",
				name: "No Model Story",
				error: null,
				merge_failure: null,
				agent: {
					agent_name: "coder-2",
					model: null,
					status: "running",
				},
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		expect(screen.getByText("coder-2")).toBeInTheDocument();
	});

	it("shows agent lozenge for pending agent", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "45_story_pending",
				name: "Pending Story",
				error: null,
				merge_failure: null,
				agent: {
					agent_name: "coder-1",
					model: "haiku",
					status: "pending",
				},
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="QA" items={items} />);
		expect(screen.getByText("coder-1 haiku")).toBeInTheDocument();
	});

	it("shows story number extracted from story_id", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "59_story_current_work_panel",
				name: "Current Work Panel",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		expect(screen.getByText("#59")).toBeInTheDocument();
	});

	it("shows error message when item has an error", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "1_story_bad",
				name: null,
				error: "Missing front matter",
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Upcoming" items={items} />);
		expect(screen.getByText("Missing front matter")).toBeInTheDocument();
	});

	it("shows STORY badge for story items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "10_story_some_feature",
				name: "Some Feature",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Upcoming" items={items} />);
		expect(
			screen.getByTestId("type-badge-10_story_some_feature"),
		).toHaveTextContent("STORY");
	});

	it("shows BUG badge for bug items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "11_bug_broken_thing",
				name: "Broken Thing",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		expect(
			screen.getByTestId("type-badge-11_bug_broken_thing"),
		).toHaveTextContent("BUG");
	});

	it("shows SPIKE badge for spike items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "12_spike_investigate_perf",
				name: "Investigate Perf",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="QA" items={items} />);
		expect(
			screen.getByTestId("type-badge-12_spike_investigate_perf"),
		).toHaveTextContent("SPIKE");
	});

	it("shows no badge for unrecognised type prefix", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "13_task_do_something",
				name: "Do Something",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Done" items={items} />);
		expect(
			screen.queryByTestId("type-badge-13_task_do_something"),
		).not.toBeInTheDocument();
	});

	it("card has uniform border on all sides for story items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "20_story_uniform_border",
				name: "Uniform Border",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Upcoming" items={items} />);
		const card = screen.getByTestId("card-20_story_uniform_border");
		// No 3px colored left border - all sides match the uniform shorthand
		expect(card.style.borderLeft).not.toContain("3px");
		expect(card.style.borderLeft).toBe(card.style.borderTop);
		expect(card.style.borderLeft).toBe(card.style.borderRight);
		expect(card.style.borderLeft).toBe(card.style.borderBottom);
	});

	it("card has uniform border on all sides for bug items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "21_bug_uniform_border",
				name: "Uniform Border Bug",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Current" items={items} />);
		const card = screen.getByTestId("card-21_bug_uniform_border");
		expect(card.style.borderLeft).not.toContain("3px");
		expect(card.style.borderLeft).toBe(card.style.borderTop);
	});

	it("card has uniform border on all sides for spike items", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "22_spike_uniform_border",
				name: "Uniform Border Spike",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="QA" items={items} />);
		const card = screen.getByTestId("card-22_spike_uniform_border");
		expect(card.style.borderLeft).not.toContain("3px");
		expect(card.style.borderLeft).toBe(card.style.borderTop);
	});

	it("card has uniform border on all sides for unrecognised type", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "23_task_uniform_border",
				name: "Uniform Border Task",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Done" items={items} />);
		const card = screen.getByTestId("card-23_task_uniform_border");
		expect(card.style.borderLeft).not.toContain("3px");
		expect(card.style.borderLeft).toBe(card.style.borderTop);
	});

	it("shows merge failure icon and reason when merge_failure is set", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "30_story_merge_failed",
				name: "Failed Merge Story",
				error: null,
				merge_failure: "Squash merge failed: conflicts in Cargo.lock",
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Merge" items={items} />);
		expect(
			screen.getByTestId("merge-failure-icon-30_story_merge_failed"),
		).toBeInTheDocument();
		expect(
			screen.getByTestId("merge-failure-reason-30_story_merge_failed"),
		).toHaveTextContent("Squash merge failed: conflicts in Cargo.lock");
	});

	it("does not show merge failure elements when merge_failure is null", () => {
		const items: PipelineStageItem[] = [
			{
				story_id: "31_story_no_failure",
				name: "Clean Story",
				error: null,
				merge_failure: null,
				agent: null,
				review_hold: null,
				qa: null,
				depends_on: null,
			},
		];
		render(<StagePanel title="Merge" items={items} />);
		expect(
			screen.queryByTestId("merge-failure-icon-31_story_no_failure"),
		).not.toBeInTheDocument();
		expect(
			screen.queryByTestId("merge-failure-reason-31_story_no_failure"),
		).not.toBeInTheDocument();
	});
});
