import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { SheetForm } from "../sheet-form";

describe("SheetForm", () => {
	it("renders the title and children", () => {
		render(
			<SheetForm
				open
				onOpenChange={() => {}}
				title="Edit service"
				onSave={() => {}}
				onCancel={() => {}}
			>
				<input aria-label="Name" />
			</SheetForm>,
		);

		expect(screen.getByText("Edit service")).toBeInTheDocument();
		expect(screen.getByLabelText("Name")).toBeInTheDocument();
	});

	it("calls onSave when Save is clicked", async () => {
		const onSave = vi.fn();
		render(
			<SheetForm
				open
				onOpenChange={() => {}}
				title="Edit"
				onSave={onSave}
				onCancel={() => {}}
			>
				<div />
			</SheetForm>,
		);

		await userEvent.click(screen.getByRole("button", { name: "Save" }));
		expect(onSave).toHaveBeenCalledTimes(1);
	});

	it("calls onCancel when Cancel is clicked", async () => {
		const onCancel = vi.fn();
		render(
			<SheetForm
				open
				onOpenChange={() => {}}
				title="Edit"
				onSave={() => {}}
				onCancel={onCancel}
			>
				<div />
			</SheetForm>,
		);

		await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
		expect(onCancel).toHaveBeenCalledTimes(1);
	});

	it("disables Save and shows a pending label when isPending", () => {
		render(
			<SheetForm
				open
				onOpenChange={() => {}}
				title="Edit"
				onSave={() => {}}
				onCancel={() => {}}
				isPending
			>
				<div />
			</SheetForm>,
		);

		const saveButton = screen.getByRole("button", { name: /Saving/i });
		expect(saveButton).toBeDisabled();
		expect(screen.getByText("Saving…")).toBeInTheDocument();
	});

	it("calls onCancel when the close (X) button is clicked", async () => {
		const onCancel = vi.fn();
		render(
			<SheetForm
				open
				onOpenChange={() => {}}
				title="Edit"
				onSave={() => {}}
				onCancel={onCancel}
			>
				<div />
			</SheetForm>,
		);

		await userEvent.click(screen.getByRole("button", { name: "Close" }));
		expect(onCancel).toHaveBeenCalledTimes(1);
	});

	describe("dirty-state confirm (R4.5)", () => {
		it("prompts before discarding via Cancel when isDirty", async () => {
			const onCancel = vi.fn();
			render(
				<SheetForm
					open
					onOpenChange={() => {}}
					title="Edit"
					onSave={() => {}}
					onCancel={onCancel}
					isDirty
				>
					<div />
				</SheetForm>,
			);

			// Cancel does not immediately close; a confirm opens.
			await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
			expect(onCancel).not.toHaveBeenCalled();
			expect(
				screen.getByRole("heading", { name: "Discard changes?" }),
			).toBeInTheDocument();

			// Confirm discard -> actually closes.
			await userEvent.click(screen.getByRole("button", { name: "Discard" }));
			expect(onCancel).toHaveBeenCalledTimes(1);
		});

		it("closing the confirm without discarding keeps the form open", async () => {
			const onCancel = vi.fn();
			render(
				<SheetForm
					open
					onOpenChange={() => {}}
					title="Edit"
					onSave={() => {}}
					onCancel={onCancel}
					isDirty
				>
					<div />
				</SheetForm>,
			);

			await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
			// Two Cancel buttons now exist: the SheetForm footer and the confirm dialog.
			const cancelButtons = screen.getAllByRole("button", { name: "Cancel" });
			await userEvent.click(cancelButtons[cancelButtons.length - 1]);
			expect(onCancel).not.toHaveBeenCalled();
		});

		it("closes immediately when not dirty", async () => {
			const onCancel = vi.fn();
			render(
				<SheetForm
					open
					onOpenChange={() => {}}
					title="Edit"
					onSave={() => {}}
					onCancel={onCancel}
				>
					<div />
				</SheetForm>,
			);

			await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
			expect(onCancel).toHaveBeenCalledTimes(1);
			expect(
				screen.queryByRole("heading", { name: "Discard changes?" }),
			).not.toBeInTheDocument();
		});
	});
});
