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

/**
 * <rating-ui> — Discrete pictorial rating (stars by default).
 *
 * Supports keyboard (arrows), mouse hover preview, and 0.5-step precision.
 * The host is role=slider per ARIA APG for custom rating widgets.
 *
 *   <rating-ui value="3.5" max="5" allow-half name="review"></rating-ui>
 *   <rating-ui value="4" icon="heart" variant="accent"></rating-ui>
 *   <rating-ui value="5" readonly></rating-ui>
 */

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

export class UIRating extends UIFormElement {
  static properties = {
    ...UIFormElement.properties,
    /** value: Number — overrides UIFormElement.value (String); syncs as string on form submit. */
    value:     { type: Number,  default: 0, reflect: true },
    max:       { type: Number,  default: 5, reflect: true },
    icon:      { type: String,  default: 'star', reflect: true },
    readonly:  { type: Boolean, default: false, reflect: true },
    allowHalf: { type: Boolean, default: false, reflect: true, attribute: 'allow-half' },
    size:      { type: String,  default: 'md', reflect: true },
  };

  static template = () => null;

  #hoverValue = null;

  connected() {
    super.connected();
    this.setAttribute('role', 'slider');
    this.setAttribute('aria-orientation', 'horizontal');
    this.setAttribute('aria-valuemin', '0');
    if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', this.readonly ? '-1' : '0');
    this.addEventListener('pointermove', this.#onPointer);
    this.addEventListener('pointerleave', this.#onLeave);
    this.addEventListener('click', this.#onClick);
    this.addEventListener('keydown', this.#onKey);
  }

  render() {
    const max = Number(this.max) || 5;
    const display = this.#hoverValue ?? (Number(this.value) || 0);

    const symbols = Array.from({ length: max }, (_, i) => {
      const slotValue = i + 1;
      const fill =
        display >= slotValue        ? 'full' :
        display >= slotValue - 0.5  ? 'half' :
                                      'empty';
      return `
        <span data-rating-symbol data-fill="${fill}" aria-hidden="true">
          <icon-ui name="${this.icon}" data-rating-symbol-bg></icon-ui>
          <icon-ui name="${this.icon}" weight="fill" data-rating-symbol-fg></icon-ui>
        </span>
      `;
    }).join('');

    this.innerHTML = symbols;
    this.setAttribute('aria-valuemax', String(max));
    this.setAttribute('aria-valuenow', String(Number(this.value) || 0));
    this.setAttribute('aria-valuetext', `${Number(this.value) || 0} out of ${max}`);
    if (this.readonly) this.setAttribute('aria-readonly', 'true');
    else this.removeAttribute('aria-readonly');
  }

  #valueFromEvent(e) {
    const symbols = [...this.querySelectorAll('[data-rating-symbol]')];
    const target = symbols.find(s => s.contains(e.target));
    if (!target) return null;
    const idx = symbols.indexOf(target);
    if (idx === -1) return null;
    if (this.allowHalf) {
      const rect = target.getBoundingClientRect();
      const isHalf = (e.clientX - rect.left) < rect.width / 2;
      return idx + (isHalf ? 0.5 : 1);
    }
    return idx + 1;
  }

  #onPointer = (e) => {
    if (this.readonly || this.disabled) return;
    const v = this.#valueFromEvent(e);
    if (v == null) return;
    if (v !== this.#hoverValue) {
      this.#hoverValue = v;
      this.render();
    }
  };

  #onLeave = () => {
    if (this.#hoverValue == null) return;
    this.#hoverValue = null;
    this.render();
  };

  #onClick = (e) => {
    if (this.readonly || this.disabled) return;
    const v = this.#valueFromEvent(e);
    if (v == null) return;
    this.#commit(v);
  };

  #onKey = (e) => {
    if (this.readonly || this.disabled) return;
    const step = this.allowHalf ? 0.5 : 1;
    const max = Number(this.max) || 5;
    const v = Number(this.value) || 0;
    let next = v;
    if (e.key === 'ArrowRight' || e.key === 'ArrowUp')   { next = Math.min(max, v + step); }
    else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { next = Math.max(0, v - step); }
    else if (e.key === 'Home') next = 0;
    else if (e.key === 'End')  next = max;
    else return;
    e.preventDefault();
    this.#commit(next);
  };

  #commit(v) {
    this.value = v;
    this.#hoverValue = null;
    this.syncValue(String(v));
    this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
    this.render();
  }

  disconnected() {
    super.disconnected();
    this.removeEventListener('pointermove', this.#onPointer);
    this.removeEventListener('pointerleave', this.#onLeave);
    this.removeEventListener('click', this.#onClick);
    this.removeEventListener('keydown', this.#onKey);
  }
}
