// src/framework/components/MxSplit.ts
//
// MxSplit — Network-controllable split container.
//
// The container-owns-layout pattern: the split container manages its own
// shadow DOM sizing (handle, before/after panes). Children fill their slot
// via CSS flow. The handle is internal to shadow DOM — children never see it.
//
// Layout-only: children placed in slot="before"/slot="after" skip past
// this component when searching for their parent shell, preserving mount paths.
//
// Attributes:
//   direction   (none = horizontal top/bottom) or "vertical" (left/right)
//   position    Initial position percentage (default: 50)
//   min         Minimum position percentage (default: 10)
//   max         Maximum position percentage (default: 90)

import { MatrixActorHtmlElement } from '../MatrixActorHtmlElement';

export class MxSplit extends MatrixActorHtmlElement {
  static description = 'Draggable split pane — divides content area into resizable panels with a drag handle.';

  // Layout-only: children in light DOM slots skip past this when finding parent
  static _layoutOnly = true;

  static accepts = {
    setPosition: { position: 'number' },
    setLimits: { min: 'number?', max: 'number?' },
  };

  static emits = {
    positionChanged: { position: 'number', source: 'string' },
  };

  static moduleUrl = import.meta.url;

  static template = `
    <div class="split-container">
      <div class="split-before" id="before"><slot name="before"></slot></div>
      <div class="split-handle" id="handle"><span class="grip"></span></div>
      <div class="split-after" id="after"><slot name="after"></slot></div>
    </div>
    <style>
      :host {
        display: flex;
        flex-direction: column;
        flex: 1 1 0px;
        min-height: 0;
        min-width: 0;
        overflow: hidden;
      }
      :host([direction="vertical"]) {
        flex-direction: row;
      }
      .split-container {
        display: flex;
        flex-direction: inherit;
        flex: 1 1 0px;
        min-height: 0;
        min-width: 0;
        overflow: hidden;
      }
      .split-before, .split-after {
        overflow: hidden;
        display: flex;
        flex-direction: column;
        flex: 1 1 0px;
        min-height: 0;
        min-width: 0;
      }
      :host([direction="vertical"]) .split-before,
      :host([direction="vertical"]) .split-after {
        flex-direction: column;
      }
      ::slotted(*) {
        flex: 1 1 0px;
        min-height: 0;
        min-width: 0;
      }
      .split-handle {
        flex-shrink: 0;
        cursor: row-resize;
        background: transparent;
        display: flex;
        align-items: center;
        justify-content: center;
        height: 6px;
        margin: 0;
        transition: background 0.15s;
        user-select: none;
      }
      :host([direction="vertical"]) .split-handle {
        cursor: col-resize;
        width: 6px;
        height: auto;
        margin: 0;
        flex-direction: column;
      }
      .grip {
        display: block;
        width: 36px;
        height: 3px;
        border-radius: 2px;
        background: rgba(207, 229, 255, 0.22);
        transition: background 0.15s, width 0.15s, height 0.15s;
      }
      :host([direction="vertical"]) .grip {
        width: 3px;
        height: 36px;
      }
      .split-handle:hover, .split-handle.dragging {
        background: rgba(207, 229, 255, 0.05);
      }
      .split-handle:hover .grip, .split-handle.dragging .grip {
        background: rgba(207, 229, 255, 0.6);
        width: 52px;
      }
      :host([direction="vertical"]) .split-handle:hover .grip,
      :host([direction="vertical"]) .split-handle.dragging .grip {
        width: 4px;
        height: 52px;
      }
    </style>
  `;

  private _position = 50;
  private _min = 10;
  private _max = 90;
  private _wiredHandle: HTMLElement | null = null;

  // ── Accepts handlers ─────────────────────────────────────────────────

  onSetPosition(p: { position: number }): void {
    this._position = Math.max(this._min, Math.min(this._max, p.position));
    this._applyPosition();
    this._emitPositionChanged('message');
  }

  onSetLimits(p: { min?: number; max?: number }): void {
    if (p.min != null) this._min = p.min;
    if (p.max != null) this._max = p.max;
    this._position = Math.max(this._min, Math.min(this._max, this._position));
    this._applyPosition();
  }

  // ── Lifecycle ────────────────────────────────────────────────────────

  connectedCallback(): void {
    super.connectedCallback();
    this._renderLayoutTemplateIfNeeded();
  }

  protected async _onContextUpdated(_context: unknown): Promise<void> {
    this._readAttributes();
    this._renderLayoutTemplateIfNeeded();
    this._applyPosition();
    this._wireHandleIfNeeded();
  }

  private _renderLayoutTemplateIfNeeded(): void {
    if (!this.shadowRoot || this.shadowRoot.childNodes.length > 0) return;

    this._readAttributes();
    const tpl = document.createElement('template');
    tpl.innerHTML = MxSplit.template.trim();
    this.shadowRoot.replaceChildren(...Array.from(tpl.content.childNodes));
    this._applyPosition();
    this._wireHandleIfNeeded();
  }

  private _readAttributes(): void {
    this._position = this._readNumericAttribute('position', 50);
    this._min = this._readNumericAttribute('min', 10);
    this._max = this._readNumericAttribute('max', 90);
    this._position = Math.max(this._min, Math.min(this._max, this._position));
  }

  private _readNumericAttribute(name: string, defaultValue: number): number {
    const value = this.getAttribute(name);
    if (value == null || value.trim() === '') return defaultValue;
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : defaultValue;
  }

  private _wireHandleIfNeeded(): void {
    const handle = this.shadowRoot?.getElementById('handle');
    if (!(handle instanceof HTMLElement) || this._wiredHandle === handle) return;
    this._wiredHandle = handle;
    handle.addEventListener('pointerdown', (e: PointerEvent) => this._onPointerDown(e));
  }

  private _emitPositionChanged(source: 'message' | 'drag'): void {
    if (this.mountPath) {
      this.emit('positionChanged', { position: this._position, source });
    }
  }

  // ── Internal: position application ───────────────────────────────────

  private _applyPosition(): void {
    const before = this.shadowRoot?.getElementById('before');
    const after = this.shadowRoot?.getElementById('after');
    if (!before || !after) return;

    before.style.flex = `${this._position} 1 0px`;
    after.style.flex = `${100 - this._position} 1 0px`;
  }

  // ── Internal: drag handling ──────────────────────────────────────────

  private _onPointerDown(e: PointerEvent): void {
    e.preventDefault();
    const handle = this.shadowRoot?.getElementById('handle');
    const container = this.shadowRoot?.querySelector('.split-container');
    if (!(handle instanceof HTMLElement) || !(container instanceof HTMLElement)) return;

    handle.setPointerCapture(e.pointerId);
    handle.classList.add('dragging');

    const containerRect = container.getBoundingClientRect();
    const isVert = this.getAttribute('direction') === 'vertical';
    const containerSize = isVert ? containerRect.width : containerRect.height;
    const containerStart = isVert ? containerRect.left : containerRect.top;

    const onMove = (ev: PointerEvent) => {
      const pos = isVert ? ev.clientX : ev.clientY;
      const pct = ((pos - containerStart) / containerSize) * 100;
      this._position = Math.max(this._min, Math.min(this._max, pct));
      this._applyPosition();
    };

    const onUp = (ev: PointerEvent) => {
      handle.classList.remove('dragging');
      handle.releasePointerCapture(ev.pointerId);
      handle.removeEventListener('pointermove', onMove);
      handle.removeEventListener('pointerup', onUp);
      this._emitPositionChanged('drag');
    };

    handle.addEventListener('pointermove', onMove);
    handle.addEventListener('pointerup', onUp);
  }
}

// Alias for naming consistency with Pattern B conventions
export { MxSplit as MxSplitShell };
