/**
 * Non-side-effect class export for `<option-card-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/option-card`
 * (which imports this file + calls `defineIfFree()`).
 *
 * @see ../../USAGE.md#registration--auto-vs-explicit
 */

/**
 * <option-card-ui> — Selectable card with radio semantics.
 *
 * A "rich radio" — single-select-of-N where each option carries a
 * heading, optional description, and optional leading icon. Siblings
 * with the same `name` form a radiogroup. The whole card is the click
 * target; a CSS-rendered radio circle in the top-left signals state.
 *
 *   <option-card-ui name="use-case" value="build" checked
 *                   heading="I'm building a product"
 *                   description="Spinning up a new project — design, ship, iterate.">
 *   </option-card-ui>
 *
 * Rich content via slots:
 *
 *   <option-card-ui name="plan" value="pro">
 *     <span slot="heading">Pro <badge-ui text="14-day trial"></badge-ui></span>
 *     <span slot="description">Unlimited members · advanced charts</span>
 *   </option-card-ui>
 *
 * Spillover content via the default slot — any unslotted child shows
 * only when the card is `[checked]`. Used for "Other"-style fields:
 *
 *   <option-card-ui name="reason" value="other"
 *                   heading="Something else">
 *     <textarea-ui name="reason-detail" rows="3"></textarea-ui>
 *   </option-card-ui>
 *
 * Sibling navigation: arrow keys move focus and selection; Space/Enter
 * select. Form-associated via UIFormElement, so `name=value` submits
 * with the parent form when the card is checked.
 */

import { UIFormElement } from '../../core/form.js';
import { logicalSlotted } from '../../core/logical-children.js';

export class UIOptionCard extends UIFormElement {
  static properties = {
    ...UIFormElement.properties,
    checked:     { type: Boolean, default: false, reflect: true },
    heading:     { type: String,  default: '', reflect: true },
    description: { type: String,  default: '', reflect: true },
    icon:        { type: String,  default: '', reflect: true },
    layout:      { type: String,  default: 'default', reflect: true },
  };

  static template = () => null;

  connected() {
    super.connected();
    this.setAttribute('role', 'radio');
    this.setAttribute('tabindex', '0');
    this.#ensureLayout();
    this.addEventListener('click', this.#select);
    this.addEventListener('keydown', this.#onKey);
  }

  disconnected() {
    super.disconnected();
    this.removeEventListener('click', this.#select);
    this.removeEventListener('keydown', this.#onKey);
  }

  render() {
    this.setAttribute('aria-checked', String(this.checked));
    if (this.disabled) this.setAttribute('aria-disabled', 'true');
    else this.removeAttribute('aria-disabled');

    // Keep slotted content in sync with attrs (only when slot was
    // auto-stamped from the attr — don't clobber consumer-authored
    // rich content).
    const h = this.querySelector(':scope > [slot="heading"]');
    if (h && h.dataset.fromAttr === 'true') h.textContent = this.heading || '';
    const d = this.querySelector(':scope > [slot="description"]');
    if (d && d.dataset.fromAttr === 'true') d.textContent = this.description || '';
    const i = this.querySelector(':scope > [slot="icon"]');
    if (i && i.dataset.fromAttr === 'true') i.setAttribute('name', this.icon || '');

    if (this.checked) this.syncValue(this.value || 'on');
    else this.syncValue('');
  }

  // ── Private ───────────────────────────────────────────────────────

  /** Stamp heading / description / icon from attributes when the
   *  consumer hasn't already provided slotted content. */
  #ensureLayout() {
    if (this.heading && !logicalSlotted(this, 'heading').length) {
      const el = document.createElement('span');
      el.setAttribute('slot', 'heading');
      el.dataset.fromAttr = 'true';
      el.textContent = this.heading;
      this.appendChild(el);
    }
    if (this.description && !logicalSlotted(this, 'description').length) {
      const el = document.createElement('span');
      el.setAttribute('slot', 'description');
      el.dataset.fromAttr = 'true';
      el.textContent = this.description;
      this.appendChild(el);
    }
    if (this.icon && !logicalSlotted(this, 'icon').length) {
      const el = document.createElement('icon-ui');
      el.setAttribute('slot', 'icon');
      el.dataset.fromAttr = 'true';
      el.setAttribute('name', this.icon);
      this.appendChild(el);
    }
  }

  #select = () => {
    if (this.disabled || this.readonly || this.checked) return;
    const group = this.#group();
    if (group) {
      for (const el of group.querySelectorAll(`option-card-ui[name="${this.name}"]`)) {
        if (el !== this && el.checked) el.checked = false;
      }
    }
    this.checked = true;
    this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value, checked: this.checked } }));
  };

  #onKey = (e) => {
    // Don't intercept keys when focus is in a nested form control
    // (textarea, input, contenteditable). Otherwise Space/Enter would
    // block typing and arrow keys would navigate siblings instead of
    // moving the cursor.
    if (e.target !== this) return;
    if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); this.#select(); return; }
    if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
      e.preventDefault();
      const next = this.#sibling(1);
      if (next) { next.focus(); next.click(); }
    }
    if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
      e.preventDefault();
      const prev = this.#sibling(-1);
      if (prev) { prev.focus(); prev.click(); }
    }
  };

  /** The radiogroup root — explicit `<fieldset>` / `[role="radiogroup"]`
   *  if present, else the immediate parent. */
  #group() {
    return this.closest('fieldset, [role="radiogroup"]') || this.parentElement;
  }

  #sibling(dir) {
    const group = this.#group();
    const items = [...(group?.querySelectorAll(`option-card-ui[name="${this.name}"]`) || [])];
    const i = items.indexOf(this);
    if (i < 0) return null;
    return items[(i + dir + items.length) % items.length] || null;
  }
}
