/**
 * component-helpers.js
 * Shared utilities for panel components
 * Provides standardized loading, error, and empty states
 */

export class ComponentHelpers {
  /**
   * Render a loading spinner with optional message
   * @param {string} message - Optional loading message
   * @returns {string} HTML string for loading state
   */
  static renderLoading(message = 'Loading...') {
    return `
      <div style="
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 32px;
        color: var(--vscode-text-dim);
      ">
        <div style="
          width: 24px;
          height: 24px;
          border: 3px solid var(--vscode-border);
          border-top-color: var(--vscode-accent);
          border-radius: 50%;
          animation: spin 1s linear infinite;
        "></div>
        <div style="margin-top: 16px; font-size: 12px;">${message}</div>
      </div>
      <style>
        @keyframes spin {
          to { transform: rotate(360deg); }
        }
      </style>
    `;
  }

  /**
   * Render an error state with message
   * @param {string} message - Error message to display
   * @param {Error} error - Optional error object for details
   * @returns {string} HTML string for error state
   */
  static renderError(message, error = null) {
    const details = error ? `<div style="
      margin-top: 8px;
      padding: 8px;
      background-color: rgba(244, 135, 113, 0.1);
      border-radius: 2px;
      font-family: 'Courier New', monospace;
      font-size: 11px;
      color: #f48771;
    ">${error.message || error.toString()}</div>` : '';

    return `
      <div style="
        padding: 24px;
        text-align: center;
        color: #f48771;
      ">
        <div style="font-size: 32px; margin-bottom: 12px;">⚠️</div>
        <div style="font-size: 13px; font-weight: 500;">${message}</div>
        ${details}
      </div>
    `;
  }

  /**
   * Render an empty state with message
   * @param {string} message - Empty state message
   * @param {string} icon - Optional icon/emoji
   * @returns {string} HTML string for empty state
   */
  static renderEmpty(message = 'No data available', icon = '📭') {
    return `
      <div style="
        padding: 48px 24px;
        text-align: center;
        color: var(--vscode-text-dim);
      ">
        <div style="font-size: 48px; margin-bottom: 16px;">${icon}</div>
        <div style="font-size: 13px;">${message}</div>
      </div>
    `;
  }

  /**
   * Format timestamp for display
   * @param {Date|string|number} date - Date to format
   * @param {boolean} includeTime - Include time in output
   * @returns {string} Formatted date string
   */
  static formatTimestamp(date, includeTime = true) {
    if (!date) return 'N/A';

    const d = date instanceof Date ? date : new Date(date);

    if (isNaN(d.getTime())) return 'Invalid Date';

    const dateStr = d.toLocaleDateString('en-US', {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    });

    if (!includeTime) return dateStr;

    const timeStr = d.toLocaleTimeString('en-US', {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      hour12: false
    });

    return `${dateStr} ${timeStr}`;
  }

  /**
   * Format relative time (e.g., "2 minutes ago")
   * @param {Date|string|number} date - Date to format
   * @returns {string} Relative time string
   */
  static formatRelativeTime(date) {
    if (!date) return 'N/A';

    const d = date instanceof Date ? date : new Date(date);
    if (isNaN(d.getTime())) return 'Invalid Date';

    const now = new Date();
    const diffMs = now - d;
    const diffSec = Math.floor(diffMs / 1000);
    const diffMin = Math.floor(diffSec / 60);
    const diffHour = Math.floor(diffMin / 60);
    const diffDay = Math.floor(diffHour / 24);

    if (diffSec < 60) return 'just now';
    if (diffMin < 60) return `${diffMin} minute${diffMin !== 1 ? 's' : ''} ago`;
    if (diffHour < 24) return `${diffHour} hour${diffHour !== 1 ? 's' : ''} ago`;
    if (diffDay < 7) return `${diffDay} day${diffDay !== 1 ? 's' : ''} ago`;

    return this.formatTimestamp(d, false);
  }

  /**
   * Truncate text to max length with ellipsis
   * @param {string} text - Text to truncate
   * @param {number} maxLength - Maximum length
   * @returns {string} Truncated text
   */
  static truncateText(text, maxLength = 100) {
    if (!text || text.length <= maxLength) return text;
    return text.substring(0, maxLength - 3) + '...';
  }

  /**
   * Escape HTML to prevent XSS
   * @param {string} text - Text to escape
   * @returns {string} Escaped HTML
   */
  static escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }

  /**
   * Format file size in human-readable format
   * @param {number} bytes - Size in bytes
   * @returns {string} Formatted size string
   */
  static formatFileSize(bytes) {
    if (bytes === 0) return '0 B';
    if (!bytes) return 'N/A';

    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
  }

  /**
   * Format duration in human-readable format
   * @param {number} ms - Duration in milliseconds
   * @returns {string} Formatted duration string
   */
  static formatDuration(ms) {
    if (ms === 0) return '0ms';
    if (!ms) return 'N/A';

    if (ms < 1000) return `${Math.round(ms)}ms`;
    if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
    if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;

    const hours = Math.floor(ms / 3600000);
    const minutes = Math.floor((ms % 3600000) / 60000);
    return `${hours}h ${minutes}m`;
  }

  /**
   * Create a badge element
   * @param {string} label - Badge label
   * @param {string} type - Badge type (success, warning, error, info)
   * @returns {string} HTML string for badge
   */
  static createBadge(label, type = 'info') {
    const colors = {
      success: '#89d185',
      warning: '#dbb765',
      error: '#f48771',
      info: '#75beff'
    };

    const color = colors[type] || colors.info;

    return `
      <span style="
        display: inline-block;
        padding: 2px 8px;
        background-color: rgba(${this.hexToRgb(color)}, 0.2);
        color: ${color};
        border-radius: 2px;
        font-size: 11px;
        font-weight: 500;
        text-transform: uppercase;
        letter-spacing: 0.5px;
      ">${label}</span>
    `;
  }

  /**
   * Convert hex color to RGB values
   * @param {string} hex - Hex color
   * @returns {string} RGB values (e.g., "255, 0, 0")
   */
  static hexToRgb(hex) {
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result
      ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`
      : '128, 128, 128';
  }

  /**
   * Debounce function execution
   * @param {Function} func - Function to debounce
   * @param {number} wait - Wait time in milliseconds
   * @returns {Function} Debounced function
   */
  static debounce(func, wait = 300) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  /**
   * Parse JSON safely with fallback
   * @param {string} jsonString - JSON string to parse
   * @param {*} fallback - Fallback value if parse fails
   * @returns {*} Parsed object or fallback
   */
  static safeJsonParse(jsonString, fallback = null) {
    try {
      return JSON.parse(jsonString);
    } catch (e) {
      console.warn('Failed to parse JSON:', e);
      return fallback;
    }
  }

  /**
   * Create a table from array of objects
   * @param {Array<Object>} data - Array of objects
   * @param {Array<string>} columns - Column names to display
   * @returns {string} HTML string for table
   */
  static createTable(data, columns = null) {
    if (!data || data.length === 0) {
      return this.renderEmpty('No data to display', '📋');
    }

    const cols = columns || Object.keys(data[0]);

    const headerRow = cols.map(col =>
      `<th style="
        padding: 8px 12px;
        text-align: left;
        border-bottom: 1px solid var(--vscode-border);
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        color: var(--vscode-text-dim);
      ">${col}</th>`
    ).join('');

    const dataRows = data.map(row => `
      <tr style="border-bottom: 1px solid var(--vscode-border);">
        ${cols.map(col => `
          <td style="
            padding: 8px 12px;
            font-size: 12px;
            color: var(--vscode-text);
          ">${this.escapeHtml(String(row[col] ?? ''))}</td>
        `).join('')}
      </tr>
    `).join('');

    return `
      <div style="overflow-x: auto;">
        <table style="
          width: 100%;
          border-collapse: collapse;
          background-color: var(--vscode-sidebar);
        ">
          <thead>
            <tr>${headerRow}</tr>
          </thead>
          <tbody>
            ${dataRows}
          </tbody>
        </table>
      </div>
    `;
  }
}
