// src/flow/viewers/DataViewer.ts
//
// DataViewer - Displays structured data (JSON, objects, arrays).
// Used by ResultsPanel when rendering data results.

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

/**
 * JSON-compatible value type for data display.
 */
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };

/**
 * DataViewer - Displays structured data (JSON, objects, arrays).
 *
 * Accepts:
 *   - setData: { data: any, label?: string }
 *   - expandAll: {}
 *   - collapseAll: {}
 *   - copyToClipboard: {}
 *
 * Emits:
 *   - dataChanged: { data: any }
 *   - copied: {}
 */
export class DataViewer extends MatrixActorHtmlElement {
  static accepts = {
    setData: { data: 'any', label: 'string?' },
    expandAll: {},
    collapseAll: {},
    copyToClipboard: {},
  };

  static emits = {
    dataChanged: { data: 'any' },
    copied: {},
  };

  static template = `
    <div class="data-viewer">
      <div class="viewer-header">
        <span id="label" class="label"></span>
        <div class="actions">
          <button id="copyBtn" title="Copy to clipboard">Copy</button>
          <button id="expandBtn" title="Expand all">+</button>
          <button id="collapseBtn" title="Collapse all">-</button>
        </div>
      </div>
      <div id="content" class="viewer-content"></div>
    </div>
    <style>
      :host { display: block; }
      .data-viewer {
        border: 1px solid var(--mx-color-border-light, #e0e0e0);
        border-radius: 6px;
        background: var(--mx-color-surface-alt, #fafafa);
        overflow: hidden;
      }
      .viewer-header {
        padding: 8px 12px;
        background: var(--mx-color-surface-inset, #f5f5f5);
        border-bottom: 1px solid var(--mx-color-border-light, #e0e0e0);
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .label {
        font-weight: 500;
        color: var(--mx-color-text, #333);
        font-size: 13px;
      }
      .actions {
        display: flex;
        gap: 4px;
      }
      .actions button {
        padding: 4px 8px;
        border: 1px solid var(--mx-color-border, #ccc);
        border-radius: 4px;
        background: white;
        cursor: pointer;
        font-size: 11px;
      }
      .actions button:hover {
        background: var(--mx-color-surface-hover, #e3f2fd);
        border-color: var(--mx-color-primary-hover, #1976d2);
      }
      .viewer-content {
        padding: 12px;
        font-family: 'Consolas', 'Monaco', monospace;
        font-size: 12px;
        line-height: 1.5;
        overflow: auto;
        max-height: 400px;
        white-space: pre-wrap;
        word-break: break-word;
      }
    </style>
  `;

  private _data: JsonValue = null;
  private _label: string = 'Data';
  private _expanded = true;

  get data(): JsonValue {
    return this._data;
  }

  get label(): string {
    return this._label;
  }

  get expanded(): boolean {
    return this._expanded;
  }

  async onBootstrap(): Promise<void> {

    // Initialize from props if passed
    // Note: _initialProps is dynamically set by parent during child creation
    const props = (this as unknown as { _initialProps?: { data?: JsonValue; label?: string } })._initialProps;
    if (props?.data !== undefined) {
      this.onSetData({ data: props.data, label: props.label });
    }

    // Wire up button handlers
    const copyBtn = this.shadowRoot?.getElementById('copyBtn');
    const expandBtn = this.shadowRoot?.getElementById('expandBtn');
    const collapseBtn = this.shadowRoot?.getElementById('collapseBtn');

    copyBtn?.addEventListener('click', () => {
      navigator.clipboard?.writeText(this.getFormattedData());
      this.onCopyToClipboard();
    });

    expandBtn?.addEventListener('click', () => {
      this.onExpandAll();
      this.render();
    });

    collapseBtn?.addEventListener('click', () => {
      this.onCollapseAll();
      this.render();
    });

    // Initial render
    this.render();
  }

  onSetData(params: { data: JsonValue; label?: string }): void {
    this._data = params.data;
    if (params.label) {
      this._label = params.label;
    }
    this.emit('dataChanged', { data: this._data });
    this.render();
  }

  onExpandAll(): void {
    this._expanded = true;
  }

  onCollapseAll(): void {
    this._expanded = false;
  }

  onCopyToClipboard(): void {
    this.emit('copied', {});
  }

  /**
   * Get formatted data string for display.
   * Uses smart formatting that preserves string content at any nesting level.
   */
  getFormattedData(): string {
    return this._formatValue(this._data, 0);
  }

  /**
   * Format a value for display, preserving string formatting at any depth.
   */
  private _formatValue(value: unknown, depth: number): string {
    if (value === null || value === undefined) {
      return 'null';
    }

    // Strings display directly - preserves newlines and formatting
    if (typeof value === 'string') {
      return value;
    }

    // Numbers and booleans
    if (typeof value === 'number' || typeof value === 'boolean') {
      return String(value);
    }

    // Arrays
    if (Array.isArray(value)) {
      if (value.length === 0) return '[]';
      const indent = '  '.repeat(depth);
      const items = value.map(item => this._formatValue(item, depth + 1));
      // If all items are simple (no newlines), show compact
      if (items.every(i => !i.includes('\n') && i.length < 60)) {
        return `[${items.join(', ')}]`;
      }
      return `[\n${items.map(i => indent + '  ' + i).join(',\n')}\n${indent}]`;
    }

    // Objects - format each property, preserving string values
    if (typeof value === 'object') {
      const obj = value as Record<string, unknown>;
      const keys = Object.keys(obj);
      if (keys.length === 0) return '{}';

      const indent = '  '.repeat(depth);
      const lines: string[] = [];

      for (const key of keys) {
        const val = obj[key];
        const formattedVal = this._formatValue(val, depth + 1);

        // If value is a multi-line string, display it nicely
        if (typeof val === 'string' && val.includes('\n')) {
          lines.push(`${indent}  "${key}":`);
          lines.push(`${indent}  ┌${'─'.repeat(40)}`);
          // Indent each line of the string value
          const indentedVal = val.split('\n').map(line => indent + '  │ ' + line).join('\n');
          lines.push(indentedVal);
          lines.push(`${indent}  └${'─'.repeat(40)}`);
        } else {
          lines.push(`${indent}  "${key}": ${formattedVal}`);
        }
      }

      return `{\n${lines.join('\n')}\n${indent}}`;
    }

    // Fallback
    try {
      return JSON.stringify(value, null, 2);
    } catch {
      return String(value);
    }
  }

  /**
   * Render the component's DOM.
   */
  render(): void {
    const labelEl = this.shadowRoot?.getElementById('label');
    const contentEl = this.shadowRoot?.getElementById('content');

    if (labelEl) labelEl.textContent = this._label || 'Data';
    if (contentEl) contentEl.textContent = this.getFormattedData();
  }
}

// Keep DataViewerShell as alias for backwards compatibility
export const DataViewerShell = DataViewer;

// Register with ShellRegistry
ShellRegistry.register('DataViewer', {
  shellTag: 'data-viewer',
  moduleUrl: import.meta.url,
});

// Register custom element
if (typeof customElements !== 'undefined' && !customElements.get('data-viewer')) {
  customElements.define('data-viewer', DataViewer);
}
