import { Logger } from 'pino';
import { TOOL_NAMES } from 'chrome-mcp-shared';
import { SessionManager, ExtensionConnection } from './session-manager.js';
import { ConnectionRouter, RouteResult } from './connection-router.js';
import { LiveKitAgentManager } from './livekit-agent-manager.js';

export class ChromeTools {
  private logger: Logger;
  private sessionManager: SessionManager;
  private connectionRouter: ConnectionRouter;
  private liveKitAgentManager: LiveKitAgentManager;
  private currentUserId?: string;
  private currentSessionId?: string;

  // Common URL mappings for natural language requests
  private urlMappings: Map<string, string> = new Map([
    ['google', 'https://www.google.com'],
    ['google.com', 'https://www.google.com'],
    ['youtube', 'https://www.youtube.com'],
    ['youtube.com', 'https://www.youtube.com'],
    ['facebook', 'https://www.facebook.com'],
    ['facebook.com', 'https://www.facebook.com'],
    ['twitter', 'https://www.twitter.com'],
    ['twitter.com', 'https://www.twitter.com'],
    ['x.com', 'https://www.x.com'],
    ['github', 'https://www.github.com'],
    ['github.com', 'https://www.github.com'],
    ['stackoverflow', 'https://www.stackoverflow.com'],
    ['stackoverflow.com', 'https://www.stackoverflow.com'],
    ['reddit', 'https://www.reddit.com'],
    ['reddit.com', 'https://www.reddit.com'],
    ['amazon', 'https://www.amazon.com'],
    ['amazon.com', 'https://www.amazon.com'],
    ['netflix', 'https://www.netflix.com'],
    ['netflix.com', 'https://www.netflix.com'],
    ['linkedin', 'https://www.linkedin.com'],
    ['linkedin.com', 'https://www.linkedin.com'],
    ['instagram', 'https://www.instagram.com'],
    ['instagram.com', 'https://www.instagram.com'],
  ]);

  constructor(logger: Logger) {
    this.logger = logger;
    this.sessionManager = new SessionManager(logger);
    this.connectionRouter = new ConnectionRouter(logger, this.sessionManager);
    this.liveKitAgentManager = new LiveKitAgentManager(logger, this.sessionManager);
  }

  // Register a Chrome extension connection with session management
  registerExtension(
    connection: any,
    userId?: string,
    metadata?: any,
  ): { userId: string; sessionId: string; connectionId: string } {
    const result = this.sessionManager.registerExtensionConnection(connection, userId, metadata);
    this.logger.info(
      `🔗 Chrome extension connected - User: ${result.userId}, Session: ${result.sessionId}`,
    );

    // Note: LiveKit agent is no longer started automatically on connection
    // Agents should be started manually when needed

    return result;
  }

  // Unregister a Chrome extension connection
  unregisterExtension(connection: any): boolean {
    const result = this.sessionManager.unregisterExtensionConnection(connection);
    if (result) {
      this.logger.info('🔌 Chrome extension disconnected');
      // Note: LiveKit agent is no longer stopped automatically on disconnection
      // Agents should be managed manually when needed
    }
    return result;
  }

  // Update Chrome extension user ID
  updateExtensionUserId(connection: any, newUserId: string): any {
    const result = this.sessionManager.updateExtensionUserId(connection, newUserId);
    if (result) {
      this.logger.info(`🔄 Chrome extension user ID updated to: ${newUserId}`);
      // Note: LiveKit agent is no longer restarted automatically on user ID update
      // Agents should be managed manually when needed
    }
    return result;
  }

  // Set user context for routing
  setUserContext(userId: string, sessionId?: string) {
    this.currentUserId = userId;
    this.currentSessionId = sessionId;
    this.logger.info(`🎯 [Chrome Tools] User context set - User: ${userId}, Session: ${sessionId}`);
  }

  // Handle responses from Chrome extension
  handleResponse(data: any) {
    const stats = this.sessionManager.getStats();
    this.logger.info(`📨 [Chrome Tools] Received response from Chrome extension:`, {
      messageId: data.id,
      hasResult: !!data.result,
      hasError: !!data.error,
      pendingRequestsCount: stats.pendingRequests,
      fullData: data,
    });

    if (data.id) {
      if (data.error) {
        this.logger.error(`📨 [Chrome Tools] Chrome extension returned error: ${data.error}`);
        this.sessionManager.rejectPendingRequest(data.id, new Error(data.error));
      } else {
        this.logger.info(
          `📨 [Chrome Tools] Chrome extension returned success result:`,
          data.result,
        );
        this.sessionManager.resolvePendingRequest(data.id, data.result);
      }
    } else {
      // Filter out ping/heartbeat messages and other non-request messages to reduce noise
      const isPingMessage =
        data.type === 'ping' || (data.id && data.id.toString().startsWith('ping_'));
      const isHeartbeatMessage = !data.id || data.id === undefined;

      if (!isPingMessage && !isHeartbeatMessage) {
        this.logger.warn(
          `📨 [Chrome Tools] Received response for unknown or expired request ID: ${data.id}`,
        );
      } else {
        // Log ping/heartbeat messages at debug level to reduce noise
        this.logger.debug(
          `📨 [Chrome Tools] Received ping/heartbeat message (ID: ${data.id}, type: ${data.type})`,
        );
      }
    }
  }

  // Process natural language navigation requests
  private processNavigationRequest(args: any): any {
    if (!args || !args.url) {
      return args;
    }

    const url = args.url.toLowerCase().trim();

    // Check if it's a natural language request like "google", "open google", etc.
    const patterns = [/^(?:open\s+|go\s+to\s+|navigate\s+to\s+)?(.+?)(?:\.com)?$/i, /^(.+?)$/i];

    for (const pattern of patterns) {
      const match = url.match(pattern);
      if (match) {
        const site = match[1].toLowerCase().trim();
        const mappedUrl = this.urlMappings.get(site);
        if (mappedUrl) {
          this.logger.info(`Mapped natural language request "${url}" to "${mappedUrl}"`);
          return { ...args, url: mappedUrl };
        }
      }
    }

    // If no mapping found, check if it's already a valid URL
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
      // Try to make it a valid URL
      const processedUrl = url.includes('.')
        ? `https://${url}`
        : `https://www.google.com/search?q=${encodeURIComponent(url)}`;
      this.logger.info(`Processed URL "${url}" to "${processedUrl}"`);
      return { ...args, url: processedUrl };
    }

    return args;
  }

  // Send a general tool call to Chrome extension with routing
  async callTool(name: string, args: any, sessionId?: string, userId?: string): Promise<any> {
    // Use current user context if not provided
    const effectiveUserId = userId || this.currentUserId;
    const effectiveSessionId = sessionId || this.currentSessionId;

    this.logger.info(`🔧 [Chrome Tools] Calling tool: ${name} with routing context:`, {
      args,
      sessionId: effectiveSessionId,
      userId: effectiveUserId,
      usingCurrentContext: !userId && !sessionId,
    });

    const message = {
      action: 'callTool',
      params: { name, arguments: args },
    };

    this.logger.info(`🔧 [Chrome Tools] Sending routed message to extensions:`, message);

    const result = await this.sendToExtensions(message, effectiveSessionId, effectiveUserId);

    this.logger.info(`🔧 [Chrome Tools] Received result from extensions:`, result);

    return result;
  }

  // Get session statistics
  getSessionStats(): any {
    return this.sessionManager.getStats();
  }

  // Get routing statistics
  getRoutingStats(): any {
    return this.connectionRouter.getRoutingStats();
  }

  // Get connection by session ID
  getConnectionBySessionId(sessionId: string): ExtensionConnection | null {
    return this.sessionManager.getConnectionBySessionId(sessionId);
  }

  // Get connection by user ID
  getConnectionByUserId(userId: string): ExtensionConnection | null {
    return this.sessionManager.getConnectionByUserId(userId);
  }

  // Route message to specific connection type
  async callToolWithConnectionType(
    name: string,
    args: any,
    connectionType: 'newest' | 'oldest' | 'most_active',
  ): Promise<any> {
    this.logger.info(
      `🔧 [Chrome Tools] Calling tool: ${name} with connection type: ${connectionType}`,
    );

    const message = {
      action: 'callTool',
      params: { name, arguments: args },
    };

    const routeResult = this.connectionRouter.routeToConnectionType(message, connectionType);
    const result = await this.sendToExtensions(message, routeResult.sessionId);

    this.logger.info(`🔧 [Chrome Tools] Tool result from ${connectionType} connection:`, result);
    return result;
  }

  // Check if session can handle message
  canSessionHandleMessage(sessionId: string, messageType: string): boolean {
    return this.connectionRouter.canSessionHandleMessage(sessionId, messageType);
  }

  // Get recommended session for user
  getRecommendedSessionForUser(userId: string): string | null {
    return this.connectionRouter.getRecommendedSessionForUser(userId);
  }

  // Get LiveKit agent for user
  getLiveKitAgentForUser(userId: string): any {
    return this.liveKitAgentManager.getAgentForUser(userId);
  }

  // Get LiveKit agent statistics
  getLiveKitAgentStats(): any {
    return this.liveKitAgentManager.getAgentStats();
  }

  // Get all active LiveKit agents
  getAllActiveLiveKitAgents(): any[] {
    return this.liveKitAgentManager.getAllActiveAgents();
  }

  // Cleanup resources
  destroy(): void {
    this.connectionRouter.cleanupRoutingRules();
    this.liveKitAgentManager.shutdownAllAgents();
    this.sessionManager.destroy();
  }

  // Send a message to Chrome extensions with intelligent routing
  private async sendToExtensions(message: any, sessionId?: string, userId?: string): Promise<any> {
    const stats = this.sessionManager.getStats();
    this.logger.info(`📤 [Chrome Tools] Routing message to Chrome extensions:`, {
      action: message.action,
      connectionsCount: stats.activeConnections,
      sessionId,
      userId,
      fullMessage: message,
    });

    if (stats.activeConnections === 0) {
      this.logger.error('🚫 [Chrome Tools] No Chrome extensions connected');
      throw new Error('No Chrome extensions connected');
    }

    // Use connection router to find the best connection
    let routeResult: RouteResult;
    try {
      routeResult = this.connectionRouter.routeMessage(message, sessionId, userId);
    } catch (error) {
      this.logger.error('Failed to route message:', error);
      throw error;
    }

    const { connection: extensionConnection, routingReason } = routeResult;
    const connection = extensionConnection.connection;
    const readyState = (connection as any).readyState;

    this.logger.info(
      `📤 [Chrome Tools] Routed to connection - Session: ${extensionConnection.sessionId}, User: ${extensionConnection.userId}, Reason: ${routingReason}, ReadyState: ${readyState}`,
    );

    return new Promise((resolve, reject) => {
      const messageId = Date.now().toString() + Math.random().toString(36).substring(2, 11);
      const messageWithId = { ...message, id: messageId };

      // Store the request with session context
      this.sessionManager.storePendingRequest(
        messageId,
        resolve,
        reject,
        extensionConnection.sessionId,
        60000, // 60 second timeout
      );

      try {
        // Check if connection is still open before sending
        if (readyState === 1) {
          // WebSocket.OPEN
          this.logger.info(
            `📤 [Chrome Tools] Sending message with ID ${messageId} to Chrome extension (Session: ${extensionConnection.sessionId}, Routing: ${routingReason}):`,
            messageWithId,
          );
          (connection as any).send(JSON.stringify(messageWithId));
        } else {
          this.sessionManager.rejectPendingRequest(
            messageId,
            new Error(`Chrome extension connection is not open (readyState: ${readyState})`),
          );
        }
      } catch (error) {
        this.sessionManager.rejectPendingRequest(messageId, error);
      }
    });
  }

  async navigateToUrl(url: string): Promise<any> {
    this.logger.info(`Navigating to URL: ${url}`);

    // Process natural language navigation requests
    const processedArgs = this.processNavigationRequest({ url });

    return await this.sendToExtensions({
      action: 'navigate',
      params: processedArgs,
    });
  }

  async getPageContent(selector?: string): Promise<any> {
    this.logger.info(`Getting page content${selector ? ` with selector: ${selector}` : ''}`);

    return await this.sendToExtensions({
      action: 'getContent',
      params: { selector },
    });
  }

  async clickElement(selector: string): Promise<any> {
    this.logger.info(`Clicking element: ${selector}`);

    return await this.sendToExtensions({
      action: 'click',
      params: { selector },
    });
  }

  async fillInput(selector: string, value: string): Promise<any> {
    this.logger.info(`Filling input ${selector} with value: ${value}`);

    return await this.sendToExtensions({
      action: 'fillInput',
      params: { selector, value },
    });
  }

  async takeScreenshot(fullPage: boolean = false): Promise<any> {
    this.logger.info(`Taking screenshot (fullPage: ${fullPage})`);

    return await this.sendToExtensions({
      action: 'screenshot',
      params: { fullPage },
    });
  }

  async executeScript(script: string): Promise<any> {
    this.logger.info('Executing script');

    return await this.sendToExtensions({
      action: 'executeScript',
      params: { script },
    });
  }

  async getCurrentTab(): Promise<any> {
    this.logger.info('Getting current tab info');

    return await this.sendToExtensions({
      action: 'getCurrentTab',
      params: {},
    });
  }

  async getAllTabs(): Promise<any> {
    this.logger.info('Getting all tabs');

    return await this.sendToExtensions({
      action: 'getAllTabs',
      params: {},
    });
  }

  async switchToTab(tabId: number): Promise<any> {
    this.logger.info(`Switching to tab: ${tabId}`);

    return await this.sendToExtensions({
      action: 'switchTab',
      params: { tabId },
    });
  }

  async createNewTab(url?: string): Promise<any> {
    this.logger.info(`Creating new tab${url ? ` with URL: ${url}` : ''}`);

    return await this.sendToExtensions({
      action: 'createTab',
      params: { url },
    });
  }

  async closeTab(tabId?: number): Promise<any> {
    this.logger.info(`Closing tab${tabId ? `: ${tabId}` : ' (current)'}`);

    return await this.sendToExtensions({
      action: 'closeTab',
      params: { tabId },
    });
  }

  // Browser automation tools matching the native server functionality

  async getWindowsAndTabs(): Promise<any> {
    this.logger.info('Getting all windows and tabs');
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.GET_WINDOWS_AND_TABS,
      params: {},
    });
  }

  async searchTabsContent(query: string): Promise<any> {
    this.logger.info(`Searching tabs content for: ${query}`);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.SEARCH_TABS_CONTENT,
      params: { query },
    });
  }

  async chromeNavigate(args: any): Promise<any> {
    this.logger.info(`Chrome navigate with args:`, args);

    // Process natural language navigation requests
    const processedArgs = this.processNavigationRequest(args);

    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NAVIGATE,
      params: processedArgs,
    });
  }

  async chromeScreenshot(args: any): Promise<any> {
    this.logger.info(`Chrome screenshot with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.SCREENSHOT,
      params: args,
    });
  }

  async chromeCloseTabs(args: any): Promise<any> {
    this.logger.info(`Chrome close tabs with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.CLOSE_TABS,
      params: args,
    });
  }

  async chromeGoBackOrForward(args: any): Promise<any> {
    this.logger.info(`Chrome go back/forward with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.GO_BACK_OR_FORWARD,
      params: args,
    });
  }

  async chromeGetWebContent(args: any): Promise<any> {
    this.logger.info(`Chrome get web content with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.WEB_FETCHER,
      params: args,
    });
  }

  async chromeClickElement(args: any): Promise<any> {
    this.logger.info(`Chrome click element with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.CLICK,
      params: args,
    });
  }

  async chromeFillOrSelect(args: any): Promise<any> {
    this.logger.info(`Chrome fill or select with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.FILL,
      params: args,
    });
  }

  async chromeGetInteractiveElements(args: any): Promise<any> {
    this.logger.info(`Chrome get interactive elements with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.GET_INTERACTIVE_ELEMENTS,
      params: args,
    });
  }

  async chromeNetworkCaptureStart(args: any): Promise<any> {
    this.logger.info(`Chrome network capture start with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NETWORK_CAPTURE_START,
      params: args,
    });
  }

  async chromeNetworkCaptureStop(args: any): Promise<any> {
    this.logger.info(`Chrome network capture stop with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NETWORK_CAPTURE_STOP,
      params: args,
    });
  }

  async chromeNetworkRequest(args: any): Promise<any> {
    this.logger.info(`Chrome network request with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NETWORK_REQUEST,
      params: args,
    });
  }

  async chromeNetworkDebuggerStart(args: any): Promise<any> {
    this.logger.info(`Chrome network debugger start with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NETWORK_DEBUGGER_START,
      params: args,
    });
  }

  async chromeNetworkDebuggerStop(args: any): Promise<any> {
    this.logger.info(`Chrome network debugger stop with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.NETWORK_DEBUGGER_STOP,
      params: args,
    });
  }

  async chromeKeyboard(args: any): Promise<any> {
    this.logger.info(`Chrome keyboard with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.KEYBOARD,
      params: args,
    });
  }

  async chromeHistory(args: any): Promise<any> {
    this.logger.info(`Chrome history with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.HISTORY,
      params: args,
    });
  }

  async chromeBookmarkSearch(args: any): Promise<any> {
    this.logger.info(`Chrome bookmark search with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.BOOKMARK_SEARCH,
      params: args,
    });
  }

  async chromeBookmarkAdd(args: any): Promise<any> {
    this.logger.info(`Chrome bookmark add with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.BOOKMARK_ADD,
      params: args,
    });
  }

  async chromeBookmarkDelete(args: any): Promise<any> {
    this.logger.info(`Chrome bookmark delete with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.BOOKMARK_DELETE,
      params: args,
    });
  }

  async chromeInjectScript(args: any): Promise<any> {
    this.logger.info(`Chrome inject script with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.INJECT_SCRIPT,
      params: args,
    });
  }

  async chromeSendCommandToInjectScript(args: any): Promise<any> {
    this.logger.info(`Chrome send command to inject script with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.SEND_COMMAND_TO_INJECT_SCRIPT,
      params: args,
    });
  }

  async chromeConsole(args: any): Promise<any> {
    this.logger.info(`Chrome console with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.CONSOLE,
      params: args,
    });
  }

  async chromeSearchGoogle(args: any): Promise<any> {
    this.logger.info(`Chrome search Google with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.SEARCH_GOOGLE,
      params: args,
    });
  }

  async chromeSubmitForm(args: any): Promise<any> {
    this.logger.info(`Chrome submit form with args:`, args);
    return await this.sendToExtensions({
      action: TOOL_NAMES.BROWSER.SUBMIT_FORM,
      params: args,
    });
  }
}
