/**
 * UIFormElement — base class for form-bearing primitives.
 *
 * Extends UIElement with `ElementInternals` form participation, value sync,
 * constraint validation, and the standard validity surface. 15 primitives
 * extend this: input, select, slider, switch, segmented, check, radio,
 * textarea, range, color-picker, rating, option-card, search, otp-input, code.
 *
 * @see ../USAGE.md#form-participation
 */

import { UIElement, type PropertiesMap } from './element.js';

/**
 * Form-participating reactive base class. Subclass to author a form-bearing
 * primitive. `static formAssociated = true` is already set; consumer subclasses
 * shouldn't override it.
 *
 * @example
 *   class MyField extends UIFormElement {
 *     static properties = {
 *       ...UIFormElement.properties,
 *       maxItems: { type: Number, default: 10 },
 *     };
 *   }
 */
export class UIFormElement extends UIElement {
  /** Marks the element as form-associated. Do not override. */
  static readonly formAssociated: true;

  /**
   * Base property set inherited by every form-bearing primitive. Subclasses
   * spread this into their own `static properties`.
   */
  static properties: PropertiesMap & {
    name: { type: StringConstructor; default: ''; reflect: true };
    value: { type: StringConstructor; default: ''; reflect: true };
    disabled: { type: BooleanConstructor; default: false; reflect: true };
    required: { type: BooleanConstructor; default: false; reflect: true };
    readonly: { type: BooleanConstructor; default: false; reflect: true };
    error: { type: StringConstructor; default: ''; reflect: true };
    hint: { type: StringConstructor; default: ''; reflect: true };
    pattern: { type: StringConstructor; default: ''; reflect: true };
    minlength: { type: NumberConstructor; default: null; reflect: true };
    maxlength: { type: NumberConstructor; default: null; reflect: true };
    throttle: { type: NumberConstructor; default: 0; reflect: true };
  };

  // ── Inherited UIFormElement properties (typed) ──
  /** Form field name — submitted as key in FormData. */
  name: string;
  /** Submitted value. Subclasses may narrow the type (e.g. `slider-ui` to `number`). */
  value: string;
  /** Disabled state — reflected, blocks interaction. */
  disabled: boolean;
  /** Required state — checked against value emptiness on validation. */
  required: boolean;
  /** Read-only state — reflected, blocks user input but submits value. */
  readonly: boolean;
  /** Current error message — surfaces via `:invalid` + reportValidity. */
  error: string;
  /** Hint text — usually rendered below the control. */
  hint: string;
  /** Regex pattern — value must match if set. */
  pattern: string;
  /** Minimum length — `null` to disable. */
  minlength: number | null;
  /** Maximum length — `null` to disable. */
  maxlength: number | null;
  /**
   * §220 (v0.5.9, FEEDBACK-14 §3). Trailing-debounce on the `input` event by
   * N ms. `0` = no throttle (default; preserves synchronous emission). Useful
   * for expensive `input`-driven computation (server-side autocomplete, large
   * list filter, palette regen). `change` fires unthrottled; any pending
   * `input` flushes before `change` via `flushPendingInput()`.
   *
   * §247 (v0.5.10, FB-25): declared on the base class so all 17 form-bearing
   * primitives inherit the type via TS `extends`. The runtime already worked
   * via `...UIFormElement.properties` spread; this declaration closes the
   * matching type surface.
   */
  throttle: number;

  /**
   * §220 (v0.5.9). Schedule a trailing-debounced `input` dispatch when
   * `throttle > 0`; fire synchronously when `0`. Subclasses call this from
   * their internal `input`-event handlers.
   */
  scheduleThrottledInput(detail?: { value: string | number } | null): void;

  /**
   * §220 (v0.5.9). Synchronously flush any pending throttled `input` dispatch.
   * Call from `change` / blur / explicit-commit paths so consumers see the
   * trailing `input` BEFORE the commit. No-op when no timer is pending.
   */
  flushPendingInput(detail?: { value: string | number } | null): void;

  /**
   * §220 (v0.5.9). Drop any pending throttled `input` dispatch without firing.
   * Called automatically from `disconnected()` to avoid late dispatches after
   * teardown.
   */
  dropPendingInput(): void;

  // ── ElementInternals accessors ──

  /** The form this element is associated with, or `null` if not in a form. */
  readonly form: HTMLFormElement | null;
  /** Labels associated with this element via `for` / wrapping. */
  readonly labels: NodeList;
  /** Current validity state. */
  readonly validity: ValidityState;
  /** Localized validation message. */
  readonly validationMessage: string;
  /** Whether the element will be validated when its form is submitted. */
  readonly willValidate: boolean;

  /** Returns true if the element passes all constraint validations. */
  checkValidity(): boolean;

  /** Like `checkValidity()` but also surfaces error UI if invalid. */
  reportValidity(): boolean;

  // ── Manual validation control ──

  /**
   * Run constraint validation against the current value. Returns true if valid.
   * Sets `error` + surfaces UI if invalid.
   */
  validate(): boolean;

  /** Mark the element invalid with a custom error message. */
  setInvalid(message: string): void;

  /** Clear the invalid state. */
  setValid(): void;

  /**
   * Update the form-submission value + run constraint checks. Called internally
   * on input/change; consumers rarely call directly.
   */
  syncValue(value: string): void;
}
