import { Textfield, ValidationMessage } from "@digdir/designsystemet-react";
import { type FilterTree, getLocalizedString, toFilterTree } from "@olenbetong/appframe-core";
import { useFilterConversion } from "@olenbetong/appframe-react";
import { useEffect, useState } from "react";

import type { FilterEditorSize } from "./types.js";

export type FilterStringFieldProps = {
	filter: FilterTree;
	onChange: (filter: FilterTree) => void;
	viewName?: string;
	readOnly?: boolean;
	"data-size"?: FilterEditorSize;
};

/**
 * Two-way filter string editor. The tree is the source of truth: it is
 * converted to a string whenever it changes. Editing the string and leaving the
 * field parses it back into a tree.
 */
export function FilterStringField({
	filter,
	onChange,
	viewName,
	readOnly,
	"data-size": size = "sm",
}: FilterStringFieldProps) {
	let { filterString, isConverting, error, warning, parseFilterString } = useFilterConversion(filter, { viewName });
	let [draft, setDraft] = useState<string | null>(null);
	let [parseError, setParseError] = useState<string | null>(null);

	// The tree changed underneath us, so any half-typed string is stale.
	useEffect(() => {
		setDraft(null);
		setParseError(null);
	}, [filter]);

	async function commit() {
		if (draft === null || draft === filterString) {
			setDraft(null);
			return;
		}

		let parsed = await parseFilterString(draft);

		if (parsed) {
			setDraft(null);
			setParseError(null);
			onChange(toFilterTree(parsed));
		} else {
			setParseError(getLocalizedString("The filter could not be parsed"));
		}
	}

	let message = parseError ?? error?.message ?? warning;

	return (
		<div className="ObFilterEditor-filterString">
			<Textfield
				multiline
				rows={3}
				data-size={size}
				readOnly={readOnly}
				label={
					<span className="ObFilterEditor-filterStringLabel">
						{getLocalizedString("Filter string")}
						{isConverting && <span className="ObFilterEditor-hint">{getLocalizedString("Converting…")}</span>}
					</span>
				}
				value={draft ?? filterString}
				onChange={(event) => setDraft(event.target.value)}
				onBlur={commit}
			/>
			{message && <ValidationMessage data-color={parseError ? "danger" : "warning"}>{message}</ValidationMessage>}
		</div>
	);
}
