import { h } from '../core/dom';
import { attachAutofill } from './autofill';

let fieldIdSeq = 0;

export function field(label: string, control: Node, hint?: string): HTMLElement {
  const labelEl = h('label', label) as HTMLLabelElement;
  // Associate the label with the control ONLY when the control is a single
  // labelable form element. We must not wrap the control in a <label> (the
  // old behavior): a <label> implicitly targets its first labelable
  // descendant, so for a button group (e.g. segmented) iOS Safari redirects
  // every tap inside the field to the first button, making the other options
  // impossible to select. A plain <div> wrapper avoids that; the explicit
  // for/id below keeps tap-to-focus working for real inputs.
  if (control instanceof HTMLElement && /^(INPUT|SELECT|TEXTAREA)$/.test(control.tagName)) {
    if (!control.id) control.id = `field-${++fieldIdSeq}`;
    labelEl.htmlFor = control.id;
  }
  return h('div.field', labelEl, control, hint ? h('div.hint', hint) : null);
}

export function textInput(opts: { value?: string; placeholder?: string; autofill?: boolean; oninput?: (v: string) => void } = {}): HTMLInputElement {
  const el = h('input.input', {
    type: 'text',
    value: opts.value ?? '',
    placeholder: opts.placeholder ?? '',
    oninput: () => opts.oninput?.(el.value),
  }) as HTMLInputElement;
  // Opt-in soft-fill. The controller's global listeners are gated on focus, so
  // an input the properties panel creates and discards unfocused leaks nothing.
  if (opts.autofill) attachAutofill(el);
  return el;
}

export function numberInput(opts: {
  value: number; min?: number; max?: number; step?: number; suffix?: string; onchange?: (v: number) => void;
}): HTMLInputElement {
  const el = h('input.input', {
    type: 'number',
    value: String(opts.value),
    min: opts.min, max: opts.max, step: opts.step ?? 1,
    oninput: () => opts.onchange?.(parseFloat(el.value)),
  }) as HTMLInputElement;
  return el;
}

export function select(opts: {
  options: { value: string; label: string }[];
  value?: string;
  onchange?: (v: string) => void;
}): HTMLSelectElement {
  const el = h(
    'select.select',
    { onchange: () => opts.onchange?.(el.value) },
    ...opts.options.map((o) => h('option', { value: o.value, selected: o.value === opts.value }, o.label)),
  ) as HTMLSelectElement;
  if (opts.value != null) el.value = opts.value;
  return el;
}

export function checkbox(label: string, checked: boolean, onchange: (v: boolean) => void): HTMLElement {
  const input = h('input', { type: 'checkbox', checked, onchange: () => onchange(input.checked) }) as HTMLInputElement;
  return h('label.checkbox-row', input, h('span', label));
}

export function slider(opts: {
  value: number; min: number; max: number; step?: number; onchange?: (v: number) => void;
}): HTMLElement {
  const val = h('span.slider-val', String(opts.value));
  const input = h('input.slider', {
    type: 'range',
    min: opts.min, max: opts.max, step: opts.step ?? 1, value: String(opts.value),
    oninput: () => { val.textContent = input.value; opts.onchange?.(parseFloat(input.value)); },
  }) as HTMLInputElement;
  return h('div.slider-row', input, val);
}

/** A row of color swatches with an optional custom picker. */
export function colorSwatches(opts: {
  colors: string[];
  value?: string;
  allowCustom?: boolean;
  onpick: (c: string) => void;
}): HTMLElement {
  const wrap = h('div.swatches');
  const render = (active: string) => {
    wrap.replaceChildren(
      ...opts.colors.map((c) =>
        h('button.swatch' + (c === active ? ' on' : ''), {
          style: { background: c },
          'aria-label': c,
          onclick: () => { opts.onpick(c); render(c); },
        }),
      ),
    );
    if (opts.allowCustom) {
      const picker = h('input.swatch-custom', {
        type: 'color',
        value: /^#([0-9a-f]{6})$/i.test(active) ? active : '#000000',
        oninput: () => { opts.onpick(picker.value); },
      }) as HTMLInputElement;
      wrap.appendChild(h('label.swatch swatch-picker', { title: 'Custom color' }, picker));
    }
  };
  render(opts.value ?? opts.colors[0]);
  return wrap;
}

/** Segmented control (pill group). */
export function segmented(opts: {
  options: { value: string; label: string | Node }[];
  value: string;
  onchange: (v: string) => void;
}): HTMLElement {
  const wrap = h('div.segmented');
  const render = (active: string) => {
    wrap.replaceChildren(
      ...opts.options.map((o) =>
        h('button.seg' + (o.value === active ? ' on' : ''), {
          onclick: () => { opts.onchange(o.value); render(o.value); },
        }, o.label),
      ),
    );
  };
  render(opts.value);
  return wrap;
}

/** A file-size / result summary strip. */
export function resultStat(label: string, value: string, tone: 'ok' | 'ink' = 'ink'): HTMLElement {
  return h('div.result-stat', h('div.result-stat-v', { class: tone === 'ok' ? 'ok' : '' }, value), h('div.result-stat-l', label));
}
