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

/**
 * row-ui — Flex row layout primitive.
 *
 * <row-ui gap="sm" justify="space-between" wrap>...</row-ui>
 * <row-ui draggable>...</row-ui>   wires the draggable trait, emits drag-end
 */

import { UIElement } from '../../core/element.js';
import { draggable } from '../../traits/draggable/draggable.js';
import { parseResponsive, breakpoint, BP_NAMES } from '../../core/responsive.js';

export class UIRow extends UIElement {
  static properties = {
    justify:   { type: String,  default: 'start',  reflect: true },
    align:     { type: String,  default: 'center', reflect: true },
    gap:       { type: String,  default: 'md',     reflect: true },
    grow:      { type: Boolean, default: false,    reflect: true },
    wrap:      { type: Boolean, default: false,    reflect: true },
    // wrap-at="sm" = enable flex-wrap from sm upward; complements the
    // boolean [wrap] (always-wrap) without modifying it.
    wrapAt:    { type: String,  default: '',       reflect: true, attribute: 'wrap-at' },
    draggable: { type: Boolean, default: false,    reflect: true },
  };
  static template = () => null;

  #dragAttached = false;

  connected() {
    if (this.draggable && !this.#dragAttached) {
      this.addTrait(draggable);
      this.#dragAttached = true;
    }
  }

  updated(changed) {
    if (changed.has('draggable') && this.draggable && !this.#dragAttached) {
      this.addTrait(draggable);
      this.#dragAttached = true;
    }
  }

  render() {
    const gap     = this.gap;
    const align   = this.align;
    const justify = this.justify;
    const wrapAt  = this.wrapAt;
    const anyR    = gap?.includes('@') || align?.includes('@') || justify?.includes('@') || !!wrapAt;
    const bp      = anyR ? breakpoint.value : '';

    if (gap?.includes('@')) {
      this.style.setProperty('--row-gap', _gapToCss(parseResponsive(gap, bp)));
    } else {
      this.style.removeProperty('--row-gap');
    }

    if (align?.includes('@')) {
      this.style.setProperty('--row-align', _flexAlign(parseResponsive(align, bp)));
    } else {
      this.style.removeProperty('--row-align');
    }

    if (justify?.includes('@')) {
      this.style.setProperty('--row-justify', _flexJustify(parseResponsive(justify, bp)));
    } else {
      this.style.removeProperty('--row-justify');
    }

    // wrap-at="sm" enables flex-wrap from the named breakpoint upward.
    // Uses inline style rather than toggling the [wrap] attribute to avoid
    // reactive re-entrancy. Falls back to '' (CSS [wrap] handles the
    // always-wrap case; this only fires when wrap-at is set).
    if (wrapAt) {
      const activeIdx = BP_NAMES.indexOf(bp);
      const targetIdx = BP_NAMES.indexOf(wrapAt);
      this.style.flexWrap = (targetIdx !== -1 && activeIdx >= targetIdx) ? 'wrap' : '';
    } else {
      this.style.flexWrap = '';
    }
  }
}

const _ALIGN   = { start: 'flex-start', end: 'flex-end', center: 'center', stretch: 'stretch', baseline: 'baseline' };
const _JUSTIFY = { start: 'flex-start', end: 'flex-end', center: 'center', between: 'space-between', 'space-between': 'space-between', 'space-around': 'space-around' };

function _gapToCss(v)    { return (!v || v === '0') ? '0' : /^\d+$/.test(v) ? `var(--a-space-${v})` : `var(--a-gap-${v})`; }
function _flexAlign(v)   { return _ALIGN[v]   ?? v; }
function _flexJustify(v) { return _JUSTIFY[v] ?? v; }
