// src/flow/viewers/TableViewer.ts
//
// TableViewer - Displays array data as a sortable table.

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

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

/**
 * Table row type - an object with string keys and JSON values.
 */
type TableRow = Record<string, JsonValue>;

/**
 * TableViewer - Displays array data as an interactive table.
 *
 * Accepts:
 *   - setData: { data: any[], label?: string, columns?: string[] }
 *   - sortBy: { column: string, direction?: 'asc' | 'desc' }
 *   - selectRow: { index: number }
 *
 * Emits:
 *   - rowSelected: { index: number, data: any }
 *   - sorted: { column: string, direction: string }
 */
export class TableViewer extends MatrixActorHtmlElement {
  static accepts = {
    setData: { data: 'any', label: 'string?', columns: 'any?' },
    sortBy: { column: 'string', direction: 'string?' },
    selectRow: { index: 'number' },
  };

  static emits = {
    rowSelected: { index: 'number', data: 'any' },
    sorted: { column: 'string', direction: 'string' },
  };

  static template = `
    <div class="table-viewer">
      <div class="table-header">
        <span id="label" class="label">Table</span>
        <span id="rowCount" class="row-count">0 rows</span>
      </div>
      <div class="table-container">
        <table>
          <thead id="tableHead"></thead>
          <tbody id="tableBody"></tbody>
        </table>
      </div>
    </div>
    <style>
      :host { display: block; }
      .table-viewer {
        border: 1px solid var(--mx-color-border-light, #e0e0e0);
        border-radius: 8px;
        background: var(--mx-color-surface-alt, #fafafa);
        overflow: hidden;
      }
      .table-header {
        padding: 10px 12px;
        background: linear-gradient(135deg, var(--mx-color-success, #00897b) 0%, var(--mx-color-success, #26a69a) 100%);
        color: white;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .label {
        font-weight: 600;
        font-size: 14px;
      }
      .row-count {
        font-size: 12px;
        opacity: 0.9;
      }
      .table-container {
        overflow-x: auto;
        max-height: 400px;
        overflow-y: auto;
      }
      table {
        width: 100%;
        border-collapse: collapse;
        font-size: 13px;
      }
      th, td {
        padding: 8px 12px;
        text-align: left;
        border-bottom: 1px solid var(--mx-color-border-light, #e0e0e0);
      }
      th {
        background: var(--mx-color-surface-inset, #f5f5f5);
        font-weight: 600;
        cursor: pointer;
        user-select: none;
        position: sticky;
        top: 0;
        z-index: 1;
      }
      th:hover {
        background: var(--mx-color-surface-hover, #eeeeee);
      }
      th.sorted {
        background: var(--mx-color-surface-hover, #e0f2f1);
      }
      .sort-icon {
        font-size: 10px;
        margin-left: 4px;
        opacity: 0.5;
      }
      th.sorted .sort-icon {
        opacity: 1;
      }
      tr:hover {
        background: var(--mx-color-surface-inset, #f5f5f5);
      }
      tr.selected {
        background: var(--mx-color-surface-hover, #e0f2f1);
      }
      .cell-null {
        color: var(--mx-color-text-muted, #999);
        font-style: italic;
      }
      .cell-boolean {
        color: var(--mx-color-primary-hover, #7b1fa2);
      }
      .cell-number {
        color: var(--mx-color-warning, #f57c00);
        font-family: monospace;
      }
      .cell-object {
        color: var(--mx-color-text-secondary, #666);
        font-family: monospace;
        font-size: 11px;
      }
      .cell-string {
        color: var(--mx-color-text, #333);
      }
    </style>
  `;

  private _data: TableRow[] = [];
  private _label: string = 'Table';
  private _columns: string[] = [];
  private _sortColumn: string | null = null;
  private _sortDirection: 'asc' | 'desc' = 'asc';
  private _selectedIndex: number | null = null;

  get data(): TableRow[] {
    return this._data;
  }

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

  get columns(): string[] {
    return this._columns;
  }

  get sortColumn(): string | null {
    return this._sortColumn;
  }

  get sortDirection(): 'asc' | 'desc' {
    return this._sortDirection;
  }

  get selectedIndex(): number | null {
    return this._selectedIndex;
  }

  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?: TableRow[]; label?: string; columns?: string[] } })._initialProps;
    if (props?.data !== undefined) {
      this.onSetData({ data: props.data, label: props.label, columns: props.columns });
    }

    // Wire up sorting via header clicks
    const tableHead = this.shadowRoot?.getElementById('tableHead');
    const tableBody = this.shadowRoot?.getElementById('tableBody');

    tableHead?.addEventListener('click', (e: Event) => {
      const th = (e.target as HTMLElement).closest('th');
      if (th) {
        const column = th.dataset.column;
        if (column) {
          this.onSortBy({ column });
          this.render();
        }
      }
    });

    // Wire up row selection
    tableBody?.addEventListener('click', (e: Event) => {
      const tr = (e.target as HTMLElement).closest('tr');
      if (tr) {
        const index = parseInt(tr.dataset.index || '0', 10);
        this.onSelectRow({ index });
        this.render();
      }
    });

    // Initial render
    this.render();
  }

  onSetData(params: { data: TableRow[] | TableRow; label?: string; columns?: string[] }): void {
    this._data = Array.isArray(params.data) ? params.data : [params.data];
    if (params.label) {
      this._label = params.label;
    }

    // Auto-detect columns from first row
    if (params.columns) {
      this._columns = params.columns;
    } else if (this._data.length > 0 && typeof this._data[0] === 'object') {
      this._columns = Object.keys(this._data[0]);
    } else {
      this._columns = ['value'];
    }

    this.render();
  }

  onSortBy(params: { column: string; direction?: 'asc' | 'desc' }): void {
    const { column, direction } = params;

    if (this._sortColumn === column && !direction) {
      // Toggle direction
      this._sortDirection = this._sortDirection === 'asc' ? 'desc' : 'asc';
    } else {
      this._sortColumn = column;
      this._sortDirection = direction || 'asc';
    }

    this.emit('sorted', { column: this._sortColumn, direction: this._sortDirection });

    // Re-render to apply the sort (needed for external commands via transport)
    this.render();
  }

  onSelectRow(params: { index: number }): void {
    this._selectedIndex = params.index;
    if (this._data[params.index]) {
      this.emit('rowSelected', { index: params.index, data: this._data[params.index] });
    }
  }

  /**
   * Get sorted data for rendering
   */
  getSortedData(): TableRow[] {
    if (!this._sortColumn) {
      return this._data;
    }

    return [...this._data].sort((a, b) => {
      const aVal = a[this._sortColumn!];
      const bVal = b[this._sortColumn!];

      if (aVal === bVal) return 0;
      if (aVal === null || aVal === undefined) return 1;
      if (bVal === null || bVal === undefined) return -1;

      const comparison = aVal < bVal ? -1 : 1;
      return this._sortDirection === 'asc' ? comparison : -comparison;
    });
  }

  /**
   * Format cell value for display
   */
  formatCellValue(value: JsonValue): { html: string; className: string } {
    if (value === null || value === undefined) {
      return { html: 'null', className: 'cell-null' };
    }
    if (typeof value === 'boolean') {
      return { html: value ? '✓ true' : '✗ false', className: 'cell-boolean' };
    }
    if (typeof value === 'number') {
      return { html: value.toLocaleString(), className: 'cell-number' };
    }
    if (typeof value === 'object') {
      return { html: JSON.stringify(value), className: 'cell-object' };
    }
    return { html: this._escapeHtml(String(value)), className: 'cell-string' };
  }

  private _escapeHtml(str: string): string {
    return str.replace(/&/g, '&amp;')
              .replace(/</g, '&lt;')
              .replace(/>/g, '&gt;')
              .replace(/"/g, '&quot;');
  }

  /**
   * Render the table
   */
  render(): void {
    const labelEl = this.shadowRoot?.getElementById('label');
    const rowCountEl = this.shadowRoot?.getElementById('rowCount');
    const tableHead = this.shadowRoot?.getElementById('tableHead');
    const tableBody = this.shadowRoot?.getElementById('tableBody');

    if (labelEl) labelEl.textContent = this._label || 'Table';
    if (rowCountEl) rowCountEl.textContent = `${this._data?.length || 0} rows`;

    if (this._data && Array.isArray(this._data) && this._data.length > 0) {
      const columns = this._columns;
      const sortCol = this._sortColumn;
      const sortDir = this._sortDirection;

      if (tableHead) {
        tableHead.innerHTML = `<tr>${columns.map(col => {
          const isSorted = col === sortCol;
          const icon = isSorted ? (sortDir === 'asc' ? '▲' : '▼') : '⇅';
          return `<th data-column="${col}" class="${isSorted ? 'sorted' : ''}">${col} <span class="sort-icon">${icon}</span></th>`;
        }).join('')}</tr>`;
      }

      if (tableBody) {
        const sortedData = this.getSortedData();
        tableBody.innerHTML = sortedData.map((row: TableRow, idx: number) => {
          const isSelected = idx === this._selectedIndex;
          return `<tr class="${isSelected ? 'selected' : ''}" data-index="${idx}">${columns.map(col => {
            const cell = this.formatCellValue(row[col]);
            return `<td class="${cell.className}">${cell.html}</td>`;
          }).join('')}</tr>`;
        }).join('');
      }
    } else {
      if (tableHead) tableHead.innerHTML = '';
      if (tableBody) tableBody.innerHTML = '<tr><td style="text-align:center;color:var(--mx-color-text-muted, #999);padding:20px;">No data</td></tr>';
    }
  }
}

// Keep TableViewerShell as alias for backwards compatibility
export const TableViewerShell = TableViewer;

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

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