/**
 * Remote Server Client for Chrome Extension
 * Connects to the remote MCP server via WebSocket
 */

import { TIMEOUTS } from '@/common/constants';
import { handleCallTool } from '@/entrypoints/background/tools';
import { DEFAULT_CONNECTION_CONFIG } from '@/common/env-config';

export interface RemoteServerConfig {
  serverUrl: string;
  reconnectInterval: number;
  maxReconnectAttempts: number;
}

export interface SessionInfo {
  userId: string;
  sessionId: string;
  connectionId: string;
}

export interface RemoteServerStatus {
  connected: boolean;
  connecting: boolean;
  connectionTime?: number;
  reconnectAttempts: number;
  error?: string;
  serverUrl: string;
  sessionInfo?: SessionInfo;
}

export class RemoteServerClient {
  private ws: WebSocket | null = null;
  private config: RemoteServerConfig;
  private reconnectAttempts = 0;
  private isConnecting = false;
  private messageHandlers = new Map<string, (data: any) => void>();
  private connectionTime: number | null = null;
  private statusUpdateCallback: ((status: any) => void) | null = null;
  private persistentConnectionEnabled = true; // Enable persistent connections by default
  private connectionStateKey = 'remoteServerConnectionState';
  private keepAliveInterval: NodeJS.Timeout | null = null;
  private sessionInfo: SessionInfo | null = null;

  constructor(config: Partial<RemoteServerConfig> = {}) {
    this.config = {
      serverUrl: config.serverUrl || DEFAULT_CONNECTION_CONFIG.serverUrl,
      reconnectInterval: config.reconnectInterval || DEFAULT_CONNECTION_CONFIG.reconnectInterval,
      maxReconnectAttempts: config.maxReconnectAttempts || 999999, // Effectively unlimited for persistent connections
    };

    // Do not auto-connect on initialization - only connect when user explicitly requests it
    console.log(
      'RemoteServerClient: Initialized without auto-connection. Use connect() method to establish connection.',
    );
  }

  /**
   * Set up keep-alive mechanism to prevent connection timeouts
   */
  private setupKeepAlive(): void {
    // Clear any existing keep-alive interval
    if (this.keepAliveInterval) {
      clearInterval(this.keepAliveInterval);
    }

    // Send a ping every 25 seconds to keep connection alive
    this.keepAliveInterval = setInterval(() => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        // Send a simple ping message with proper ID to avoid "unknown request ID" warnings
        try {
          const pingId = `ping_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
          this.ws.send(
            JSON.stringify({
              id: pingId,
              type: 'ping',
              timestamp: Date.now(),
            }),
          );
        } catch (error) {
          console.warn('RemoteServerClient: Failed to send keep-alive ping:', error);
        }
      }
    }, 25000); // 25 seconds - slightly less than server's 30-second ping interval
  }

  /**
   * Clear keep-alive mechanism
   */
  private clearKeepAlive(): void {
    if (this.keepAliveInterval) {
      clearInterval(this.keepAliveInterval);
      this.keepAliveInterval = null;
    }
  }

  setStatusUpdateCallback(callback: (status: any) => void) {
    this.statusUpdateCallback = callback;
  }

  /**
   * Save connection state to chrome storage for persistence
   */
  private async saveConnectionState(): Promise<void> {
    try {
      const state = {
        wasConnected: this.isConnected(),
        connectionTime: this.connectionTime,
        serverUrl: this.config.serverUrl,
        lastSaveTime: Date.now(),
      };
      await chrome.storage.local.set({ [this.connectionStateKey]: state });
      console.log('RemoteServerClient: Connection state saved', state);
    } catch (error) {
      console.warn('RemoteServerClient: Failed to save connection state:', error);
    }
  }

  /**
   * Load connection state from chrome storage (for manual restoration only)
   */
  private async loadConnectionState(): Promise<any> {
    try {
      const result = await chrome.storage.local.get(this.connectionStateKey);
      const state = result[this.connectionStateKey];

      if (state && state.wasConnected) {
        console.log(
          'RemoteServerClient: Found previous connection state (manual restoration available)',
          state,
        );
        return state;
      }
      return null;
    } catch (error) {
      console.warn('RemoteServerClient: Failed to load connection state:', error);
      return null;
    }
  }

  /**
   * Manually restore connection from saved state (only when user requests it)
   */
  async restoreConnectionFromState(): Promise<boolean> {
    const state = await this.loadConnectionState();
    if (!state) {
      console.log('RemoteServerClient: No previous connection state to restore');
      return false;
    }

    const timeSinceLastSave = Date.now() - (state.lastSaveTime || 0);
    const maxRestoreAge = 24 * 60 * 60 * 1000; // 24 hours

    if (timeSinceLastSave < maxRestoreAge) {
      console.log('RemoteServerClient: Attempting to restore connection from saved state...');
      try {
        await this.connect();
        return true;
      } catch (error) {
        console.log('RemoteServerClient: Failed to restore previous connection:', error);
        return false;
      }
    } else {
      console.log('RemoteServerClient: Previous connection state too old, clearing...');
      await this.clearConnectionState();
      return false;
    }
  }

  /**
   * Clear saved connection state
   */
  private async clearConnectionState(): Promise<void> {
    try {
      await chrome.storage.local.remove(this.connectionStateKey);
      console.log('RemoteServerClient: Connection state cleared');
    } catch (error) {
      console.warn('RemoteServerClient: Failed to clear connection state:', error);
    }
  }

  private notifyStatusUpdate(status: any) {
    if (this.statusUpdateCallback) {
      this.statusUpdateCallback(status);
    }

    // Also send to popup if available
    try {
      chrome.runtime.sendMessage(
        {
          type: 'remoteServerStatusUpdate',
          payload: status,
        },
        (response) => {
          // Handle response or check for errors
          if (chrome.runtime.lastError) {
            // Silently ignore "Receiving end does not exist" errors
            // This happens when popup is not open
          }
        },
      );
    } catch (error) {
      // Ignore errors if popup is not available
    }
  }

  getStatus(): RemoteServerStatus {
    return {
      connected: this.isConnected(),
      connecting: this.isConnecting,
      reconnectAttempts: this.reconnectAttempts,
      connectionTime: this.connectionTime,
      serverUrl: this.config.serverUrl,
      sessionInfo: this.sessionInfo,
    };
  }

  /**
   * Get current session information
   */
  getSessionInfo(): SessionInfo | null {
    return this.sessionInfo;
  }

  /**
   * Check if session is active
   */
  hasActiveSession(): boolean {
    return this.sessionInfo !== null && this.isConnected();
  }

  /**
   * Generate or retrieve persistent user ID for this Chrome extension
   */
  private async generateOrRetrieveUserId(): Promise<string> {
    const storageKey = 'chrome_extension_user_id';

    try {
      // Try to get existing user ID from storage first
      const result = await chrome.storage.local.get([storageKey]);
      if (result[storageKey]) {
        console.log(`Chrome Extension: Retrieved existing user ID: ${result[storageKey]}`);
        return result[storageKey];
      }
    } catch (error) {
      console.warn('Failed to retrieve user ID from storage:', error);
    }

    // Generate new user ID if none exists
    const timestamp = Date.now();
    const randomSuffix = Math.random().toString(36).substring(2, 15);
    const userId = `user_${timestamp}_${randomSuffix}`;

    try {
      // Save the new user ID to storage for persistence
      await chrome.storage.local.set({ [storageKey]: userId });
      console.log(`Chrome Extension: Generated and saved new user ID: ${userId}`);
    } catch (error) {
      console.warn('Failed to save user ID to storage:', error);
    }

    return userId;
  }

  /**
   * Get the current user ID (public method)
   */
  public async getCurrentUserId(): Promise<string | null> {
    const storageKey = 'chrome_extension_user_id';
    try {
      const result = await chrome.storage.local.get([storageKey]);
      return result[storageKey] || null;
    } catch (error) {
      console.warn('Failed to get current user ID:', error);
      return null;
    }
  }

  async connect(): Promise<void> {
    // Check if already connected or connecting
    if (this.isConnecting) {
      console.log('Connection attempt already in progress');
      return;
    }

    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      console.log('Already connected to remote server');
      return;
    }

    // Clean up any existing connection in bad state
    if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
      console.log('Cleaning up existing connection in bad state');
      this.ws.close();
      this.ws = null;
    }

    this.isConnecting = true;
    this.notifyStatusUpdate({ connecting: true, error: undefined });

    try {
      console.log(`Attempting to connect to: ${this.config.serverUrl}`);
      this.ws = new WebSocket(this.config.serverUrl);

      this.ws.onopen = async () => {
        console.log('Connected to remote MCP server');
        this.isConnecting = false;
        this.reconnectAttempts = 0;
        this.connectionTime = Date.now();

        // Generate or retrieve persistent user ID for this Chrome extension
        const userId = await this.generateOrRetrieveUserId();

        // Send connection info to server for session management with user ID
        const connectionInfo = {
          type: 'connection_info',
          userId: userId, // Include the Chrome extension's user ID
          userAgent: navigator.userAgent,
          timestamp: Date.now(),
          extensionId: chrome.runtime.id,
        };

        console.log(`Chrome Extension: Connecting with user ID: ${userId}`);
        this.ws?.send(JSON.stringify(connectionInfo));

        this.notifyStatusUpdate({
          connected: true,
          connecting: false,
          connectionTime: this.connectionTime,
          reconnectAttempts: this.reconnectAttempts,
        });

        // Start keep-alive mechanism
        this.setupKeepAlive();

        // Save connection state for persistence
        this.saveConnectionState();
      };

      this.ws.onmessage = (event) => {
        try {
          // Handle ping/pong for connection keep-alive
          if (event.data === 'ping') {
            this.ws?.send('pong');
            return;
          }

          const data = JSON.parse(event.data);

          // Check if this is a session initialization message
          if (data.type === 'session_info' && data.sessionInfo) {
            this.sessionInfo = data.sessionInfo;
            console.log('Session info received:', this.sessionInfo);
            this.notifyStatusUpdate({
              connected: true,
              connecting: false,
              connectionTime: this.connectionTime,
              reconnectAttempts: this.reconnectAttempts,
              sessionInfo: this.sessionInfo,
            });
            return;
          }

          this.handleMessage(data);
        } catch (error) {
          console.error('Error parsing message from remote server:', error);
        }
      };

      this.ws.onclose = (event) => {
        const wasConnected = this.connectionTime !== null;
        console.log(
          `Disconnected from remote MCP server (code: ${event.code}, reason: ${event.reason})`,
        );

        this.isConnecting = false;
        this.connectionTime = null;

        // Stop keep-alive mechanism
        this.clearKeepAlive();

        // Determine if this was an unexpected disconnection
        const wasUnexpected = wasConnected && event.code !== 1000; // 1000 = normal closure

        this.notifyStatusUpdate({
          connected: false,
          connecting: false,
          connectionTime: null,
          error: wasUnexpected ? `Connection lost (code: ${event.code})` : undefined,
        });

        // Only schedule reconnect for unexpected disconnections
        if (wasUnexpected) {
          this.scheduleReconnect();
        }
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        this.isConnecting = false;

        // Provide more specific error messages based on the error
        let errorMessage = 'Connection error';
        if (this.ws?.readyState === WebSocket.CONNECTING) {
          errorMessage = 'Failed to connect to server';
        } else if (this.ws?.readyState === WebSocket.OPEN) {
          errorMessage = 'Connection error during communication';
        }

        this.notifyStatusUpdate({
          connected: false,
          connecting: false,
          error: errorMessage,
        });
      };

      // Wait for connection to open with enhanced error handling
      await new Promise<void>((resolve, reject) => {
        if (!this.ws) {
          reject(new Error('WebSocket not initialized'));
          return;
        }

        const timeout = setTimeout(() => {
          this.isConnecting = false;
          if (this.ws && this.ws.readyState === WebSocket.CONNECTING) {
            this.ws.close();
          }
          this.notifyStatusUpdate({
            connected: false,
            connecting: false,
            error: 'Connection timeout - Server may be unreachable',
          });
          reject(new Error('Connection timeout - Server may be unreachable'));
        }, TIMEOUTS.REMOTE_SERVER_CONNECTION);

        // Override the onopen handler for the promise
        const originalOnOpen = this.ws.onopen;
        this.ws.onopen = (event) => {
          clearTimeout(timeout);
          // Call the original handler
          if (originalOnOpen) {
            originalOnOpen.call(this.ws, event);
          }
          resolve();
        };

        // Override the onerror handler for the promise
        const originalOnError = this.ws.onerror;
        this.ws.onerror = (event) => {
          clearTimeout(timeout);
          this.isConnecting = false;

          // Determine error type based on readyState
          let errorMessage = 'Connection failed';
          if (this.ws?.readyState === WebSocket.CONNECTING) {
            errorMessage = 'Failed to connect - Server may be offline';
          }

          this.notifyStatusUpdate({
            connected: false,
            connecting: false,
            error: errorMessage,
          });

          // Call the original handler
          if (originalOnError) {
            originalOnError.call(this.ws, event);
          }

          reject(new Error(errorMessage));
        };
      });
    } catch (error) {
      this.isConnecting = false;
      this.notifyStatusUpdate({
        connected: false,
        connecting: false,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  private scheduleReconnect(): void {
    // For persistent connections, never give up - keep trying indefinitely
    const maxAttempts = this.persistentConnectionEnabled ? Infinity : 10;

    // Only stop if persistent connections are disabled and max attempts reached
    if (!this.persistentConnectionEnabled && this.reconnectAttempts >= maxAttempts) {
      console.error(`Max reconnection attempts reached (${maxAttempts})`);
      this.notifyStatusUpdate({
        connected: false,
        connecting: false,
        reconnectAttempts: this.reconnectAttempts,
        error: `Connection failed after ${maxAttempts} attempts`,
      });

      this.clearConnectionState();
      return;
    }

    this.reconnectAttempts++;

    // Use exponential backoff for reconnection delays, but cap at reasonable intervals
    const baseDelay = this.config.reconnectInterval;
    const maxDelay = this.persistentConnectionEnabled ? 30000 : 30000; // Cap at 30s for both
    const exponentialDelay = Math.min(
      baseDelay * Math.pow(1.5, Math.min(this.reconnectAttempts - 1, 10)), // Slower growth, cap exponential part
      maxDelay,
    );

    const attemptsDisplay = this.persistentConnectionEnabled
      ? this.reconnectAttempts
      : `${this.reconnectAttempts}/${maxAttempts}`;
    console.log(
      `Scheduling reconnection attempt ${attemptsDisplay} in ${exponentialDelay}ms (persistent: ${this.persistentConnectionEnabled})`,
    );

    this.notifyStatusUpdate({
      connected: false,
      connecting: false,
      reconnectAttempts: this.reconnectAttempts,
      error: `Reconnecting... (attempt ${attemptsDisplay})`,
    });

    setTimeout(() => {
      // Check if we should still attempt reconnection
      if (this.reconnectAttempts <= maxAttempts && !this.isConnected()) {
        console.log(`Executing reconnection attempt ${this.reconnectAttempts}`);
        this.connect().catch((error) => {
          console.error(`Reconnection attempt ${this.reconnectAttempts} failed:`, error);
          // The error will trigger another reconnection attempt via onclose handler
        });
      } else {
        console.log('Skipping reconnection attempt - already connected or max attempts reached');
      }
    }, exponentialDelay);
  }

  private handleMessage(data: any): void {
    console.log('🟡 [Chrome Extension] Received message from remote server:', {
      action: data.action,
      id: data.id,
      hasParams: !!data.params,
      fullMessage: data,
    });

    if (data.id && this.messageHandlers.has(data.id)) {
      const handler = this.messageHandlers.get(data.id);
      if (handler) {
        console.log(
          '🟡 [Chrome Extension] Handling message with existing handler for ID:',
          data.id,
        );
        handler(data);
        this.messageHandlers.delete(data.id);
      }
      return;
    }

    // Handle different types of messages
    switch (data.action) {
      // General tool call handler - routes to the extension's tool system
      case 'callTool':
        console.log('🟡 [Chrome Extension] Handling callTool action:', data.params);
        this.handleToolCall(data);
        break;

      // Legacy actions for backward compatibility
      case 'navigate':
        this.handleNavigate(data);
        break;
      case 'getContent':
        this.handleGetContent(data);
        break;
      case 'click':
        this.handleClick(data);
        break;
      case 'fillInput':
        this.handleFillInput(data);
        break;
      case 'screenshot':
        this.handleScreenshot(data);
        break;
      case 'executeScript':
        this.handleExecuteScript(data);
        break;
      case 'getCurrentTab':
        this.handleGetCurrentTab(data);
        break;
      case 'getAllTabs':
        this.handleGetAllTabs(data);
        break;
      case 'switchTab':
        this.handleSwitchTab(data);
        break;
      case 'createTab':
        this.handleCreateTab(data);
        break;
      case 'closeTab':
        this.handleCloseTab(data);
        break;

      // Browser automation tools matching native server
      case 'get_windows_and_tabs':
        this.handleGetWindowsAndTabs(data);
        break;
      case 'search_tabs_content':
        this.handleSearchTabsContent(data);
        break;
      case 'chrome_navigate':
        this.handleChromeNavigate(data);
        break;
      case 'chrome_screenshot':
        this.handleChromeScreenshot(data);
        break;
      case 'chrome_close_tabs':
        this.handleChromeCloseTabs(data);
        break;
      case 'chrome_go_back_or_forward':
        this.handleChromeGoBackOrForward(data);
        break;
      case 'chrome_get_web_content':
        this.handleChromeGetWebContent(data);
        break;
      case 'chrome_click_element':
        this.handleChromeClickElement(data);
        break;
      case 'chrome_fill_or_select':
        this.handleChromeFillOrSelect(data);
        break;
      case 'chrome_get_interactive_elements':
        this.handleChromeGetInteractiveElements(data);
        break;
      case 'chrome_network_capture_start':
        this.handleChromeNetworkCaptureStart(data);
        break;
      case 'chrome_network_capture_stop':
        this.handleChromeNetworkCaptureStop(data);
        break;
      case 'chrome_network_request':
        this.handleChromeNetworkRequest(data);
        break;
      case 'chrome_network_debugger_start':
        this.handleChromeNetworkDebuggerStart(data);
        break;
      case 'chrome_network_debugger_stop':
        this.handleChromeNetworkDebuggerStop(data);
        break;
      case 'chrome_keyboard':
        this.handleChromeKeyboard(data);
        break;
      case 'chrome_history':
        this.handleChromeHistory(data);
        break;
      case 'chrome_bookmark_search':
        this.handleChromeBookmarkSearch(data);
        break;
      case 'chrome_bookmark_add':
        this.handleChromeBookmarkAdd(data);
        break;
      case 'chrome_bookmark_delete':
        this.handleChromeBookmarkDelete(data);
        break;
      case 'chrome_inject_script':
        this.handleChromeInjectScript(data);
        break;
      case 'chrome_send_command_to_inject_script':
        this.handleChromeSendCommandToInjectScript(data);
        break;
      case 'chrome_console':
        this.handleChromeConsole(data);
        break;
      default:
        console.warn('Unknown action:', data.action);
        this.sendResponse(data.id, { error: 'Unknown action' });
    }
  }

  private sendResponse(id: string, result: any): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      try {
        console.log(`📤 [Chrome Extension] Sending response for ID ${id}:`, {
          messageId: id,
          hasResult: !!result,
          isError: result?.isError,
          result,
        });
        this.ws.send(JSON.stringify({ id, result }));
      } catch (error) {
        console.error(`📤 [Chrome Extension] Failed to send response for ID ${id}:`, error);
      }
    } else {
      console.error(
        `📤 [Chrome Extension] Cannot send response for ID ${id}: WebSocket not open (readyState: ${this.ws?.readyState})`,
      );
    }
  }

  private sendError(id: string, error: string): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      try {
        console.log(`📤 [Chrome Extension] Sending error for ID ${id}:`, error);
        this.ws.send(JSON.stringify({ id, error }));
      } catch (sendError) {
        console.error(`📤 [Chrome Extension] Failed to send error for ID ${id}:`, sendError);
      }
    } else {
      console.error(
        `📤 [Chrome Extension] Cannot send error for ID ${id}: WebSocket not open (readyState: ${this.ws?.readyState})`,
      );
    }
  }

  // Chrome API handlers
  private async handleNavigate(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        await chrome.tabs.update(tabs[0].id, { url: data.params.url });
        this.sendResponse(data.id, { success: true, url: data.params.url });
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Navigation failed');
    }
  }

  private async handleGetContent(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        const results = await chrome.scripting.executeScript({
          target: { tabId: tabs[0].id! },
          func: (selector?: string) => {
            if (selector) {
              const element = document.querySelector(selector);
              return element ? element.textContent : null;
            }
            return document.body.textContent;
          },
          args: [data.params.selector],
        });

        this.sendResponse(data.id, { content: results[0].result });
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get content');
    }
  }

  private async handleClick(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        const results = await chrome.scripting.executeScript({
          target: { tabId: tabs[0].id! },
          func: (selector: string) => {
            const element = document.querySelector(selector) as HTMLElement;
            if (element) {
              element.click();
              return { success: true };
            }
            return { success: false, error: 'Element not found' };
          },
          args: [data.params.selector],
        });

        this.sendResponse(data.id, results[0].result);
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Click failed');
    }
  }

  private async handleFillInput(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        const results = await chrome.scripting.executeScript({
          target: { tabId: tabs[0].id! },
          func: (selector: string, value: string) => {
            const element = document.querySelector(selector) as HTMLInputElement;
            if (element) {
              element.value = value;
              element.dispatchEvent(new Event('input', { bubbles: true }));
              element.dispatchEvent(new Event('change', { bubbles: true }));
              return { success: true };
            }
            return { success: false, error: 'Element not found' };
          },
          args: [data.params.selector, data.params.value],
        });

        this.sendResponse(data.id, results[0].result);
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Fill input failed');
    }
  }

  private async handleScreenshot(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        const dataUrl = await chrome.tabs.captureVisibleTab(undefined, {
          format: 'png',
          quality: 90,
        });

        this.sendResponse(data.id, { screenshot: dataUrl });
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Screenshot failed');
    }
  }

  private async handleExecuteScript(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      if (tabs[0]) {
        const results = await chrome.scripting.executeScript({
          target: { tabId: tabs[0].id! },
          func: new Function('return ' + data.params.script)(),
        });

        this.sendResponse(data.id, { result: results[0].result });
      } else {
        this.sendError(data.id, 'No active tab found');
      }
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Script execution failed');
    }
  }

  private async handleGetCurrentTab(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
      this.sendResponse(data.id, { tab: tabs[0] || null });
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get current tab');
    }
  }

  private async handleGetAllTabs(data: any): Promise<void> {
    try {
      const tabs = await chrome.tabs.query({});
      this.sendResponse(data.id, { tabs });
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get all tabs');
    }
  }

  private async handleSwitchTab(data: any): Promise<void> {
    try {
      await chrome.tabs.update(data.params.tabId, { active: true });
      this.sendResponse(data.id, { success: true });
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to switch tab');
    }
  }

  private async handleCreateTab(data: any): Promise<void> {
    try {
      const tab = await chrome.tabs.create({ url: data.params.url });
      this.sendResponse(data.id, { tab });
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to create tab');
    }
  }

  private async handleCloseTab(data: any): Promise<void> {
    try {
      const tabId = data.params.tabId;
      if (tabId) {
        await chrome.tabs.remove(tabId);
      } else {
        const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
        if (tabs[0]) {
          await chrome.tabs.remove(tabs[0].id!);
        }
      }
      this.sendResponse(data.id, { success: true });
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to close tab');
    }
  }

  // Browser automation tool handlers matching native server functionality

  private async handleGetWindowsAndTabs(data: any): Promise<void> {
    try {
      const result = await handleCallTool({ name: 'get_windows_and_tabs', args: data.params });
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to get windows and tabs',
      );
    }
  }

  private async handleSearchTabsContent(data: any): Promise<void> {
    try {
      // Import handleCallTool directly to avoid message passing issues
      const { handleCallTool } = await import('../entrypoints/background/tools');
      const result = await handleCallTool({
        name: 'search_tabs_content',
        args: data.params,
      });
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to search tabs content',
      );
    }
  }

  private async handleChromeNavigate(data: any): Promise<void> {
    try {
      console.log('🔧 [Chrome Extension] handleChromeNavigate called with:', data.params);
      console.log(
        '🔧 [Chrome Extension] handleCallTool function available:',
        typeof handleCallTool,
      );

      const result = await handleCallTool({ name: 'chrome_navigate', args: data.params });

      console.log('🔧 [Chrome Extension] handleCallTool result:', result);
      this.sendResponse(data.id, result);
    } catch (error) {
      console.error('🔧 [Chrome Extension] handleChromeNavigate error:', error);
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to navigate');
    }
  }

  private async handleChromeScreenshot(data: any): Promise<void> {
    try {
      const result = await handleCallTool({ name: 'chrome_screenshot', args: data.params });
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to take screenshot');
    }
  }

  private async handleChromeCloseTabs(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_close_tabs', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to close tabs');
    }
  }

  private async handleChromeGoBackOrForward(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_go_back_or_forward', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to go back or forward',
      );
    }
  }

  private async handleChromeGetWebContent(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_get_web_content', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get web content');
    }
  }

  private async handleChromeClickElement(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_click_element', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to click element');
    }
  }

  private async handleChromeFillOrSelect(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_fill_or_select', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to fill or select');
    }
  }

  private async handleChromeGetInteractiveElements(data: any): Promise<void> {
    try {
      // Import handleCallTool directly to avoid message passing issues
      const { handleCallTool } = await import('../entrypoints/background/tools');
      const result = await handleCallTool({
        name: 'chrome_get_interactive_elements',
        args: data.params,
      });
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to get interactive elements',
      );
    }
  }

  private async handleChromeNetworkCaptureStart(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_network_capture_start', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to start network capture',
      );
    }
  }

  private async handleChromeNetworkCaptureStop(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_network_capture_stop', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to stop network capture',
      );
    }
  }

  private async handleChromeNetworkRequest(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_network_request', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to make network request',
      );
    }
  }

  private async handleChromeNetworkDebuggerStart(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_network_debugger_start', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to start network debugger',
      );
    }
  }

  private async handleChromeNetworkDebuggerStop(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_network_debugger_stop', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to stop network debugger',
      );
    }
  }

  private async handleChromeKeyboard(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_keyboard', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to simulate keyboard',
      );
    }
  }

  private async handleChromeHistory(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_history', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get history');
    }
  }

  private async handleChromeBookmarkSearch(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_bookmark_search', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to search bookmarks',
      );
    }
  }

  private async handleChromeBookmarkAdd(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_bookmark_add', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to add bookmark');
    }
  }

  private async handleChromeBookmarkDelete(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_bookmark_delete', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to delete bookmark');
    }
  }

  private async handleChromeInjectScript(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_inject_script', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to inject script');
    }
  }

  private async handleChromeSendCommandToInjectScript(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler(
        'chrome_send_command_to_inject_script',
        data.params,
      );
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(
        data.id,
        error instanceof Error ? error.message : 'Failed to send command to inject script',
      );
    }
  }

  private async handleChromeConsole(data: any): Promise<void> {
    try {
      const result = await this.callToolHandler('chrome_console', data.params);
      this.sendResponse(data.id, result);
    } catch (error) {
      this.sendError(data.id, error instanceof Error ? error.message : 'Failed to get console');
    }
  }

  // Helper method to call the extension's tool handler directly (avoiding message passing issues)
  private async callToolHandler(toolName: string, params: any): Promise<any> {
    try {
      // Import handleCallTool directly to avoid message passing issues
      const { handleCallTool } = await import('../entrypoints/background/tools');
      return await handleCallTool({
        name: toolName,
        args: params,
      });
    } catch (error) {
      throw new Error(error instanceof Error ? error.message : 'Tool execution failed');
    }
  }

  // Handle general tool calls from remote server
  private async handleToolCall(data: any): Promise<void> {
    try {
      console.log('🔧 [Chrome Extension] Handling tool call:', {
        toolName: data.params?.name,
        hasArgs: !!(data.params?.arguments || data.params?.args),
        messageId: data.id,
        fullParams: data.params,
      });

      const result = await handleCallTool({
        name: data.params.name,
        args: data.params.arguments || data.params.args,
      });

      console.log('🔧 [Chrome Extension] Tool call completed, sending response:', {
        messageId: data.id,
        hasResult: !!result,
        isError: result?.isError,
        result,
      });

      this.sendResponse(data.id, result);
    } catch (error) {
      console.error('🔧 [Chrome Extension] Tool call failed:', error);
      this.sendError(data.id, error instanceof Error ? error.message : 'Tool execution failed');
    }
  }

  disconnect(): void {
    console.log('Disconnecting from remote server...');

    // Stop any ongoing connection attempts
    this.isConnecting = false;

    // Stop keep-alive mechanism
    this.clearKeepAlive();

    if (this.ws) {
      const currentState = this.ws.readyState;
      console.log(`Closing WebSocket connection (current state: ${currentState})`);

      // Only close if not already closed/closing
      if (currentState === WebSocket.OPEN || currentState === WebSocket.CONNECTING) {
        try {
          this.ws.close(1000, 'User initiated disconnect'); // Normal closure
        } catch (error) {
          console.error('Error closing WebSocket:', error);
        }
      }

      this.ws = null;
    }

    this.connectionTime = null;
    this.reconnectAttempts = 0;

    this.notifyStatusUpdate({
      connected: false,
      connecting: false,
      connectionTime: null,
      reconnectAttempts: 0,
      error: undefined, // Clear any previous errors
    });

    // Clear saved connection state since this is a manual disconnect
    this.clearConnectionState();

    console.log('Successfully disconnected from remote server');
  }

  isConnected(): boolean {
    const connected = this.ws !== null && this.ws.readyState === WebSocket.OPEN;

    // Only log if there's a state change or for debugging
    if (this.ws) {
      const stateNames = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
      const stateName = stateNames[this.ws.readyState] || 'UNKNOWN';
      console.log(`RemoteServerClient.isConnected(): ${connected} (state: ${stateName})`);
    }

    return connected;
  }

  /**
   * Enable or disable persistent connection behavior
   */
  setPersistentConnection(enabled: boolean): void {
    this.persistentConnectionEnabled = enabled;
    console.log(`RemoteServerClient: Persistent connection ${enabled ? 'enabled' : 'disabled'}`);

    if (!enabled) {
      // Clear saved state if disabling persistence
      this.clearConnectionState();
    }
  }

  /**
   * Get current persistent connection setting
   */
  isPersistentConnectionEnabled(): boolean {
    return this.persistentConnectionEnabled;
  }
}
