// text is Schritt 7 (continued) of the module-system migration: the plain
// ui_type "text" read/edit component. Only ever reached when no more
// specific rendering (label/tags/markdown/color/email/phone/url/button
// overlay, FK badge, rollup-FK badge) already claimed the cell -- see the
// "last resort" comment next to this module's usage in DataGrid.svelte.
// Duplicates formatValue() from frontend/src/lib/formatters.ts on purpose,
// same reasoning as every other guest/frontend duplication in this
// migration: this side of the module boundary can't import that file.
function formatValue(value) {
	if (value === null || value === undefined) return '';
	if (typeof value === 'boolean') return value ? 'true' : 'false';
	if (typeof value === 'object') return JSON.stringify(value);
	return String(value);
}

customElements.define(
	'text-cell',
	class extends HTMLElement {
		static get observedAttributes() {
			return ['value'];
		}
		connectedCallback() {
			this.render();
		}
		attributeChangedCallback() {
			this.render();
		}
		render() {
			let raw = null;
			try {
				raw = JSON.parse(this.getAttribute('value') ?? 'null');
			} catch {
				raw = null;
			}
			this.textContent = formatValue(raw);
		}
	}
);

// Self-contained edit widget: unlike the grid's generic <input>, this
// element owns its own draft value and dispatches "commit"/"cancel"
// CustomEvents directly instead of participating in the grid's shared
// editValue/commitEdit/handleKeydown state -- DataGrid.svelte's oncommit/
// oncancel handlers just clear editingCell and call onCellEdit.
//
// Also dispatches a plain "input" CustomEvent on every keystroke (detail.
// value = the current text) -- unused by the single-cell edit path above,
// but the *other* consumer of this same element (the "new row" draft
// editor, which has no separate edit-mode step at all: every editable
// cell is always live-bound to draftValues) needs continuous updates, not
// just a single commit. Enter/Escape inside this element still bubble as
// plain keydown events (neither handler below calls stopPropagation), so
// the draft editor's own row-level handleKeydown still fires save/cancel
// for the *whole* row exactly as it already did for the plain <input> this
// replaces -- this element only had to add the missing "keep draftValues
// in sync as you type" part.
customElements.define(
	'text-edit',
	class extends HTMLElement {
		// Real bug found live in the draft-row editor: DataGrid.svelte's
		// <svelte:element {...spread}> sets "value" via setAttribute (this
		// class defines no real `value` property setter), which works fine
		// for the *first* value this element ever gets, but nothing here
		// reacted to it changing again -- so after a successful draft-row
		// save resets draftValues (and re-spreads value="" onto this still-
		// mounted element), the inner <input> kept showing whatever the user
		// had last typed, even though the real state was empty. The next
		// "Speichern" click then submitted that stale-looking-but-actually-
		// empty field, saving a blank row. observedAttributes +
		// attributeChangedCallback below is the missing piece: it syncs the
		// inner <input> whenever "value" is set from outside. Guarded on an
		// actual mismatch so a normal keystroke (which already updated
		// input.value directly, before the "input" event even reaches
		// DataGrid.svelte) is a no-op here -- otherwise this would reset the
		// cursor/selection to the end of the field on every keystroke.
		static get observedAttributes() {
			return ['value'];
		}
		attributeChangedCallback(name, oldValue, newValue) {
			if (this._input && this._input.value !== (newValue ?? '')) {
				this._input.value = newValue ?? '';
			}
		}
		connectedCallback() {
			this._committed = false;
			const input = document.createElement('input');
			input.type = 'text';
			// w-full, not flex-1/min-w-0: this <input> isn't a flex child of
			// anything -- it's a plain child of this custom element, which is
			// itself the flex/block item DataGrid.svelte sizes (see its
			// "class is required here" comment next to <svelte:element
			// this={mod?.editTag}>). flex-1 here was a no-op that let the
			// input fall back to its browser-default width, spilling the
			// edit border past the column boundary; w-full correctly fills
			// whatever width this element itself was given.
			input.className = 'w-full border-2 border-brand-500 bg-surface px-2.5 py-1.5 text-sm outline-none';
			input.value = this.getAttribute('value') ?? '';
			input.addEventListener('input', () => {
				this.dispatchEvent(new CustomEvent('input', { detail: { value: input.value }, bubbles: true }));
			});
			input.addEventListener('keydown', (e) => {
				if (e.key === 'Enter') {
					e.preventDefault();
					this._commit(input.value);
				} else if (e.key === 'Escape') {
					e.preventDefault();
					this._cancel();
				}
			});
			input.addEventListener('blur', () => this._commit(input.value));
			this._input = input;
			this.appendChild(input);
			// The draft-row editor mounts every editable column's edit widget
			// at once (there's no separate "click to start editing" step like
			// the single-cell path has) -- auto-focusing unconditionally would
			// have every one of them fight over focus on mount, and whichever
			// mounts last would win, stealing focus from a column the user
			// actually wants to type into first. DataGrid.svelte only sets
			// data-autofocus="true" for the single-cell path, where exactly one
			// instance ever mounts at a time.
			if (this.getAttribute('data-autofocus') === 'true') {
				requestAnimationFrame(() => input.focus());
			}
		}
		_commit(value) {
			if (this._committed) return;
			this._committed = true;
			this.dispatchEvent(new CustomEvent('commit', { detail: { value }, bubbles: true }));
		}
		_cancel() {
			if (this._committed) return;
			this._committed = true;
			this.dispatchEvent(new CustomEvent('cancel', { bubbles: true }));
		}
	}
);
