import type { ReportParameter } from "@olenbetong/appframe-core";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";

let parameterOptions: { value: string; label: string }[] = [];

vi.mock("@olenbetong/appframe-react", async (importOriginal) => ({
	...(await importOriginal<Record<string, unknown>>()),
	useParameterOptions: () => ({ options: parameterOptions, loading: false, error: null }),
}));

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

function parameter(overrides: Partial<ReportParameter> & Pick<ReportParameter, "name">): ReportParameter {
	return {
		label: overrides.name,
		description: null,
		type: "String",
		editor: "text",
		defaultValue: null,
		items: [],
		recordSource: null,
		valueMember: null,
		displayMember: null,
		sortOrder: 0,
		lookupWidth: null,
		lookupHeight: null,
		...overrides,
	};
}

describe("ReportParameters", () => {
	it("renders nothing when the report has no parameters", () => {
		let { container } = render(<ReportParameters parameters={[]} values={{}} onChange={vi.fn()} />);

		expect(container).toBeEmptyDOMElement();
	});

	it("renders a labelled editor per parameter with the description as help text", () => {
		render(
			<ReportParameters
				parameters={[
					parameter({ name: "Period", label: "Periode", type: "Integer", editor: "number" }),
					parameter({ name: "Comment", label: "Kommentar", description: "Vises på forsiden" }),
				]}
				values={{ Period: 202601, Comment: "" }}
				onChange={vi.fn()}
			/>,
		);

		expect(screen.getByLabelText("Periode")).toHaveValue(202601);
		expect(screen.getByLabelText("Kommentar")).toBeInTheDocument();
		expect(screen.getByText("Vises på forsiden")).toBeInTheDocument();
	});

	it("coerces numeric input to a number", async () => {
		let onChange = vi.fn();
		let user = userEvent.setup();
		render(
			<ReportParameters
				parameters={[parameter({ name: "Period", type: "Integer", editor: "number" })]}
				values={{ Period: null }}
				onChange={onChange}
			/>,
		);

		await user.type(screen.getByLabelText("Period"), "7");

		expect(onChange).toHaveBeenCalledWith({ Period: 7 });
	});

	it("renders a checkbox for boolean parameters", async () => {
		let onChange = vi.fn();
		let user = userEvent.setup();
		render(
			<ReportParameters
				parameters={[parameter({ name: "IncludeClosed", type: "Boolean", editor: "boolean" })]}
				values={{ IncludeClosed: false }}
				onChange={onChange}
			/>,
		);

		await user.click(screen.getByLabelText("IncludeClosed"));

		expect(onChange).toHaveBeenCalledWith({ IncludeClosed: true });
	});

	it("renders a select for parameters with a fixed item list", async () => {
		let onChange = vi.fn();
		let user = userEvent.setup();
		render(
			<ReportParameters
				parameters={[parameter({ name: "Type", editor: "select", items: ["Alle", "Åpne"] })]}
				values={{ Type: "Alle" }}
				onChange={onChange}
			/>,
		);

		await user.selectOptions(screen.getByLabelText("Type"), "Åpne");

		expect(onChange).toHaveBeenCalledWith({ Type: "Åpne" });
	});

	it("renders lookup options from the record source", () => {
		parameterOptions = [
			{ value: "1", label: "1 - Første" },
			{ value: "2", label: "2 - Andre" },
		];
		render(
			<ReportParameters
				parameters={[
					parameter({
						name: "Type",
						editor: "lookup",
						valueMember: "ID",
						recordSource: {
							viewName: "aviw_Test_Types",
							columns: [{ name: "ID", type: "number" }],
							whereClause: null,
							filterString: null,
							maxRecords: null,
						},
					}),
				]}
				values={{ Type: "2" }}
				onChange={vi.fn()}
			/>,
		);

		expect(screen.getByLabelText("Type")).toHaveValue("2 - Andre");
	});
});
