import { Select } from "@digdir/designsystemet-react";
import { XMarkIcon } from "@navikt/aksel-icons";
import {
	changeOperator,
	createExpression,
	type FieldMetadata,
	type FilterExpression,
	type FilterOperator,
	getLocalizedString,
	getOperatorLabel,
	getOperatorsForField,
	normalizeOperator,
} from "@olenbetong/appframe-core";
import { Combobox } from "@olenbetong/appframe-ds";

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

export function getFieldLabel(field: FieldMetadata) {
	return field.caption || field.name;
}

export type FilterRowProps = {
	expression: FilterExpression;
	fields: FieldMetadata[];
	onChange: (expression: FilterExpression) => void;
	onRemove: () => void;
	getFieldOptions?: GetFieldOptions;
	readOnly?: boolean;
	"data-size"?: FilterEditorSize;
};

/** A single `field operator value` row. */
export function FilterRow({
	expression,
	fields,
	onChange,
	onRemove,
	getFieldOptions,
	readOnly,
	"data-size": size = "sm",
}: FilterRowProps) {
	let field = fields.find((item) => item.name === expression.column);
	let operator = normalizeOperator(expression.operator);
	// A filter can reference a field that is no longer in the view. Keep the row
	// editable by falling back to a string field rather than dropping it.
	let effectiveField: FieldMetadata = field ?? { name: expression.column, type: "string" };
	let operators = getOperatorsForField(effectiveField);

	if (!operators.includes(operator)) {
		operators = [...operators, operator];
	}

	function handleFieldChange(next: FieldMetadata | null) {
		if (!next) return;

		// Keep the operator when the new field supports it, so switching between
		// two fields of the same type does not reset the row.
		let operators = getOperatorsForField(next);
		let keep = operators.includes(operator) ? operator : undefined;
		let created = createExpression(next, keep);

		onChange(keep ? { ...created, value: expression.value, valueType: expression.valueType } : created);
	}

	return (
		<div className="ObFilterEditor-row">
			<Combobox
				className="ObFilterEditor-field"
				data-size={size}
				readOnly={readOnly}
				disableClearable
				aria-label={getLocalizedString("Field")}
				placeholder={getLocalizedString("Select field")}
				options={fields}
				getOptionLabel={getFieldLabel}
				getOptionKey={(option) => option.name}
				isOptionEqualToValue={(a, b) => a.name === b.name}
				value={effectiveField}
				onChange={(_event, next) => handleFieldChange(next)}
			/>
			<Select
				className="ObFilterEditor-operator"
				data-size={size}
				disabled={readOnly}
				aria-label={getLocalizedString("Operator")}
				value={operator}
				onChange={(event) => onChange(changeOperator(expression, event.target.value as FilterOperator, effectiveField))}
			>
				{operators.map((item) => (
					<Select.Option key={item} value={item}>
						{getOperatorLabel(item, effectiveField.type)}
					</Select.Option>
				))}
			</Select>
			<FilterValueEditor
				expression={expression}
				field={effectiveField}
				options={field ? getFieldOptions?.(field) : undefined}
				onChange={onChange}
				readOnly={readOnly}
				data-size={size}
			/>
			{!readOnly && (
				<button
					type="button"
					className="ObFilterEditor-iconButton"
					title={getLocalizedString("Remove condition")}
					aria-label={getLocalizedString("Remove condition")}
					onClick={onRemove}
				>
					<XMarkIcon aria-hidden />
				</button>
			)}
		</div>
	);
}
