/**
 * Non-side-effect class export for `<otp-input-ui>`.
 *
 * Importing this file gives you the class(es) without auto-registering the tag.
 * Useful for test isolation, subclassing with tag-name override, or selective
 * composition.
 *
 * The auto-register path stays at `@adia-ai/web-components/components/otp-input`
 * (which imports this file + calls `defineIfFree()`).
 *
 * @see ../../USAGE.md#registration--auto-vs-explicit
 */

/**
 * <otp-input-ui length="6"></otp-input-ui>
 *
 * One-time-password input with individual digit boxes.
 * Each digit gets its own input — auto-advances on entry,
 * backs up on delete, distributes pasted values.
 *
 * Events: 'complete' (detail: {value}), 'input' (detail: {value}), 'change' (detail: {value})
 */

import { UIFormElement } from '../../core/form.js';

export class UIOtpInput extends UIFormElement {
  static properties = {
    ...UIFormElement.properties,
    length: { type: Number, default: 6, reflect: true },
  };

  static template = () => null;

  #inputs = [];

  #onInputEvent = (e) => {
    const index = this.#inputs.indexOf(e.currentTarget);
    if (index < 0) return;
    this.#onDigitInput(e, index);
  };

  #onKeydownEvent = (e) => {
    const index = this.#inputs.indexOf(e.currentTarget);
    if (index < 0) return;
    this.#onKeydown(e, index);
  };

  #onPasteEvent = (e) => {
    this.#onPaste(e);
  };

  connected() {
    super.connected();
    this.setAttribute('role', 'group');
    this.setAttribute('aria-label', 'One-time password');
    this.#stamp();
    this.#bindInputs();
  }

  #stamp() {
    const count = this.length || 6;
    this.innerHTML = '';
    this.#inputs = [];

    for (let i = 0; i < count; i++) {
      const input = document.createElement('input');
      input.type = 'text';
      input.maxLength = 1;
      input.inputMode = 'numeric';
      input.autocomplete = 'one-time-code';
      input.setAttribute('slot', 'digit');
      input.setAttribute('aria-label', `Digit ${i + 1}`);
      this.appendChild(input);
      this.#inputs.push(input);
    }
  }

  #bindInputs() {
    for (const input of this.#inputs) {
      input.addEventListener('input', this.#onInputEvent);
      input.addEventListener('keydown', this.#onKeydownEvent);
      input.addEventListener('paste', this.#onPasteEvent);
    }
  }

  #unbindInputs() {
    for (const input of this.#inputs) {
      input.removeEventListener('input', this.#onInputEvent);
      input.removeEventListener('keydown', this.#onKeydownEvent);
      input.removeEventListener('paste', this.#onPasteEvent);
    }
  }

  render() {
    if (!this.#inputs.length) return;

    const disabled = this.disabled;
    for (const input of this.#inputs) {
      input.disabled = disabled;
    }
  }

  #onDigitInput(e, index) {
    const input = this.#inputs[index];
    // Only allow digits
    input.value = input.value.replace(/\D/g, '').slice(0, 1);

    this.#syncCombined();
    this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
    this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));

    if (input.value && index < this.#inputs.length - 1) {
      this.#inputs[index + 1].focus();
    }

    this.#checkComplete();
  }

  #onKeydown(e, index) {
    if (e.key === 'Backspace' && !this.#inputs[index].value && index > 0) {
      this.#inputs[index - 1].focus();
      this.#inputs[index - 1].value = '';
      this.#syncCombined();
      this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
    this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
    }
  }

  #onPaste(e) {
    e.preventDefault();
    const text = (e.clipboardData?.getData('text/plain') || '').replace(/\D/g, '');
    for (let i = 0; i < this.#inputs.length; i++) {
      this.#inputs[i].value = text[i] || '';
    }
    this.#syncCombined();
    this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
    this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));

    // Focus last filled or first empty
    const firstEmpty = this.#inputs.findIndex(inp => !inp.value);
    if (firstEmpty >= 0) {
      this.#inputs[firstEmpty].focus();
    } else {
      this.#inputs[this.#inputs.length - 1].focus();
    }

    this.#checkComplete();
  }

  #syncCombined() {
    const combined = this.#inputs.map(inp => inp.value).join('');
    this.value = combined;
    this.syncValue(combined);
  }

  #checkComplete() {
    const combined = this.#inputs.map(inp => inp.value).join('');
    if (combined.length === this.#inputs.length) {
      this.dispatchEvent(new CustomEvent('complete', {
        bubbles: true,
        detail: { value: combined },
      }));
    }
  }

  focus() {
    const firstEmpty = this.#inputs.findIndex(inp => !inp.value);
    (this.#inputs[firstEmpty >= 0 ? firstEmpty : 0])?.focus();
  }

  clear() {
    for (const input of this.#inputs) input.value = '';
    this.value = '';
    this.syncValue('');
    this.#inputs[0]?.focus();
  }

  disconnected() {
    super.disconnected();
    this.#unbindInputs();
    this.#inputs = [];
  }
}
