/**
 * @fileoverview OrdoJS HMR Client Runtime
 *
 * Client-side hot module replacement runtime that communicates with the dev server.
 * This code gets injected into the browser during development.
 */

/**
 * Generate HMR client runtime code
 */
export function generateHMRClientCode(port: number): string {
  return `
(function() {
  'use strict';

  // HMR Client Configuration
  const HMR_PORT = ${port};
  const RECONNECT_DELAY = 1000;
  const MAX_RECONNECT_ATTEMPTS = 10;
  const PING_INTERVAL = 30000;

  // HMR Client State
  let socket = null;
  let reconnectAttempts = 0;
  let pingTimer = null;
  let isConnected = false;
  let componentRegistry = new Map();
  let stateSnapshots = new Map();

  // HMR Update Types
  const HMRUpdateType = {
    COMPONENT_UPDATE: 'component-update',
    STYLE_UPDATE: 'style-update',
    ASSET_UPDATE: 'asset-update',
    FULL_RELOAD: 'full-reload',
    ERROR: 'error'
  };

  /**
   * Initialize HMR client
   */
  function initHMR() {
    console.log('[HMR] Initializing hot module replacement...');

    // Connect to HMR server
    connect();

    // Set up global HMR interface
    window.__ORDOJS_HMR__ = {
      registerComponent,
      updateComponent,
      preserveState,
      restoreState,
      isConnected: () => isConnected
    };

    // Override console.error to capture runtime errors
    const originalError = console.error;
    console.error = function(...args) {
      originalError.apply(console, args);

      // Send error to HMR server for better debugging
      if (isConnected && socket) {
        try {
          socket.send(JSON.stringify({
            type: 'error',
            timestamp: Date.now(),
            error: args.join(' ')
          }));
        } catch (e) {
          // Ignore send errors
        }
      }
    };
  }

  /**
   * Connect to HMR WebSocket server
   */
  function connect() {
    try {
      socket = new WebSocket(\`ws://localhost:\${HMR_PORT}\`);

      socket.onopen = function() {
        console.log('[HMR] Connected to dev server');
        isConnected = true;
        reconnectAttempts = 0;

        // Start ping timer
        startPingTimer();
      };

      socket.onmessage = function(event) {
        try {
          const message = JSON.parse(event.data);
          handleMessage(message);
        } catch (error) {
          console.error('[HMR] Failed to parse message:', error);
        }
      };

      socket.onclose = function(event) {
        console.log('[HMR] Disconnected from dev server');
        isConnected = false;
        stopPingTimer();

        // Attempt to reconnect
        if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
          setTimeout(() => {
            reconnectAttempts++;
            console.log(\`[HMR] Reconnecting... (attempt \${reconnectAttempts})\`);
            connect();
          }, RECONNECT_DELAY);
        } else {
          console.warn('[HMR] Max reconnection attempts reached. Please refresh the page.');
        }
      };

      socket.onerror = function(error) {
        console.error('[HMR] WebSocket error:', error);
      };

    } catch (error) {
      console.error('[HMR] Failed to connect:', error);
    }
  }

  /**
   * Handle messages from HMR server
   */
  function handleMessage(message) {
    switch (message.type) {
      case 'welcome':
        console.log('[HMR] Welcome message received');
        break;

      case 'pong':
        // Pong response to keep connection alive
        break;

      case HMRUpdateType.COMPONENT_UPDATE:
        handleComponentUpdate(message);
        break;

      case HMRUpdateType.STYLE_UPDATE:
        handleStyleUpdate(message);
        break;

      case HMRUpdateType.ASSET_UPDATE:
        handleAssetUpdate(message);
        break;

      case HMRUpdateType.FULL_RELOAD:
        handleFullReload(message);
        break;

      case HMRUpdateType.ERROR:
        handleError(message);
        break;

      default:
        console.log('[HMR] Unknown message type:', message.type);
    }
  }

  /**
   * Handle component update
   */
  function handleComponentUpdate(message) {
    console.log(\`[HMR] Updating component: \${message.componentName}\`);

    try {
      // Preserve component state if enabled
      if (message.preserveState) {
        preserveComponentStates(message.componentName);
      }

      // Execute the new component code
      const newComponentFactory = new Function('return ' + message.code)();

      // Find and update all instances of this component
      const instances = findComponentInstances(message.componentName);

      for (const instance of instances) {
        updateComponentInstance(instance, newComponentFactory, message.preserveState);
      }

      console.log(\`[HMR] Successfully updated \${instances.length} instance(s) of \${message.componentName}\`);

    } catch (error) {
      console.error(\`[HMR] Failed to update component \${message.componentName}:\`, error);
      // Fall back to full reload on error
      window.location.reload();
    }
  }

  /**
   * Handle style update
   */
  function handleStyleUpdate(message) {
    console.log('[HMR] Updating styles...');

    try {
      // Find existing style elements for this file
      const existingStyles = document.querySelectorAll(\`style[data-hmr-file="\${message.file}"]\`);

      // Remove existing styles
      existingStyles.forEach(style => style.remove());

      // Create new style element
      const styleElement = document.createElement('style');
      styleElement.setAttribute('data-hmr-file', message.file);
      styleElement.textContent = message.css;

      // Append to head
      document.head.appendChild(styleElement);

      console.log('[HMR] Styles updated successfully');

    } catch (error) {
      console.error('[HMR] Failed to update styles:', error);
    }
  }

  /**
   * Handle asset update
   */
  function handleAssetUpdate(message) {
    console.log(\`[HMR] Asset updated: \${message.file}\`);

    // For now, we'll do a full reload for asset updates
    // In the future, this could be more intelligent
    window.location.reload();
  }

  /**
   * Handle full reload
   */
  function handleFullReload(message) {
    console.log('[HMR] Full reload requested');
    window.location.reload();
  }

  /**
   * Handle error from server
   */
  function handleError(message) {
    console.error('[HMR] Server error:', message.error);

    // Display error overlay
    showErrorOverlay(message.error, message.file);
  }

  /**
   * Register a component with HMR
   */
  function registerComponent(name, factory, element) {
    if (!componentRegistry.has(name)) {
      componentRegistry.set(name, []);
    }

    const instances = componentRegistry.get(name);
    const instance = {
      name,
      factory,
      element,
      component: null,
      id: generateInstanceId()
    };

    instances.push(instance);

    // Create and mount the component
    instance.component = factory();
    if (instance.component && instance.component.mount) {
      instance.component.mount(element);
    }

    return instance;
  }

  /**
   * Update a component instance
   */
  function updateComponent(name, newFactory) {
    const instances = componentRegistry.get(name);
    if (!instances) return;

    for (const instance of instances) {
      updateComponentInstance(instance, newFactory, true);
    }
  }

  /**
   * Update a single component instance
   */
  function updateComponentInstance(instance, newFactory, preserveState) {
    try {
      // Preserve state if requested
      let savedState = null;
      if (preserveState && instance.component && instance.component.state) {
        savedState = { ...instance.component.state };
      }

      // Unmount old component
      if (instance.component && instance.component.unmount) {
        instance.component.unmount();
      }

      // Create new component instance
      instance.factory = newFactory;
      instance.component = newFactory();

      // Restore state if preserved
      if (savedState && instance.component && instance.component.state) {
        Object.assign(instance.component.state, savedState);
      }

      // Mount new component
      if (instance.component && instance.component.mount) {
        instance.component.mount(instance.element);
      }

    } catch (error) {
      console.error('[HMR] Failed to update component instance:', error);
      throw error;
    }
  }

  /**
   * Find all instances of a component
   */
  function findComponentInstances(componentName) {
    return componentRegistry.get(componentName) || [];
  }

  /**
   * Preserve component states
   */
  function preserveComponentStates(componentName) {
    const instances = findComponentInstances(componentName);

    for (const instance of instances) {
      if (instance.component && instance.component.state) {
        const snapshot = {
          componentId: instance.id,
          componentName: componentName,
          state: { ...instance.component.state },
          props: instance.component.props || {},
          timestamp: Date.now()
        };

        stateSnapshots.set(instance.id, snapshot);

        // Send snapshot to server
        if (isConnected && socket) {
          try {
            socket.send(JSON.stringify({
              type: 'state-snapshot',
              timestamp: Date.now(),
              snapshot
            }));
          } catch (error) {
            console.debug('[HMR] Failed to send state snapshot:', error);
          }
        }
      }
    }
  }

  /**
   * Preserve state for a specific component
   */
  function preserveState(componentId) {
    const snapshot = stateSnapshots.get(componentId);
    return snapshot ? snapshot.state : null;
  }

  /**
   * Restore state for a specific component
   */
  function restoreState(componentId, state) {
    if (state) {
      stateSnapshots.set(componentId, {
        componentId,
        componentName: 'unknown',
        state,
        props: {},
        timestamp: Date.now()
      });
    }
  }

  /**
   * Show error overlay
   */
  function showErrorOverlay(error, file) {
    // Remove existing overlay
    const existingOverlay = document.getElementById('ordojs-hmr-error-overlay');
    if (existingOverlay) {
      existingOverlay.remove();
    }

    // Create error overlay
    const overlay = document.createElement('div');
    overlay.id = 'ordojs-hmr-error-overlay';
    overlay.innerHTML = \`
      <div style="
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0, 0, 0, 0.8);
        color: white;
        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
        font-size: 14px;
        z-index: 999999;
        padding: 20px;
        box-sizing: border-box;
        overflow: auto;
      ">
        <div style="max-width: 800px; margin: 0 auto;">
          <h2 style="color: #ff6b6b; margin-top: 0;">OrdoJS HMR Error</h2>
          <p><strong>File:</strong> \${file || 'Unknown'}</p>
          <pre style="
            background: #1a1a1a;
            padding: 15px;
            border-radius: 5px;
            overflow-x: auto;
            white-space: pre-wrap;
            word-wrap: break-word;
          ">\${error}</pre>
          <button onclick="this.parentElement.parentElement.remove()" style="
            background: #4CAF50;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            margin-top: 15px;
          ">Dismiss</button>
        </div>
      </div>
    \`;

    document.body.appendChild(overlay);

    // Auto-dismiss after 10 seconds
    setTimeout(() => {
      if (overlay.parentElement) {
        overlay.remove();
      }
    }, 10000);
  }

  /**
   * Start ping timer to keep connection alive
   */
  function startPingTimer() {
    stopPingTimer();
    pingTimer = setInterval(() => {
      if (isConnected && socket && socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({
          type: 'ping',
          timestamp: Date.now()
        }));
      }
    }, PING_INTERVAL);
  }

  /**
   * Stop ping timer
   */
  function stopPingTimer() {
    if (pingTimer) {
      clearInterval(pingTimer);
      pingTimer = null;
    }
  }

  /**
   * Generate unique instance ID
   */
  function generateInstanceId() {
    return 'hmr_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 8);
  }

  // Initialize HMR when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initHMR);
  } else {
    initHMR();
  }

})();
`;
}
