class EveAIChatWidget extends HTMLElement {
  static get observedAttributes() {
    return [
      'tenant-id',
      'session-token',
      'language',
      'languages',
      'specialist-id',
      'server-url'
    ];
  }

  constructor() {
    super();

    // Networking attributes
    this.socket = null; // Initialize socket to null
    this.room = null;
    this.lastRoom = null; // Store last known room
    this.userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // Detect user's timezone
    this.heartbeatInterval = null;
    this.idleTime = 0; // in milliseconds
    this.maxConnectionIdleTime = 1 * 60 * 60 * 1000; // 1 hour in milliseconds
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;

    // EveAI specific attributes
    this.languages = []
    this.currentLanguage = null;
    this.specialistId = null;
    console.log('EveAIChatWidget constructor called');

    // Bind methods to ensure correct 'this' context
    this.handleSendMessage = this.handleSendMessage.bind(this);
    this.handleTokenUpdate = this.handleTokenUpdate.bind(this);
    this.updateAttributes = this.updateAttributes.bind(this);
  }

  connectedCallback() {
    console.log('Chat Widget Connected');
    this.innerHTML = this.getTemplate();
    this.setupElements()
    this.addEventListeners()

    if (this.areAllAttributesSet()) {
        console.log('All attributes are set, populating language dropdown');
        this.populateLanguageDropdown()
        console.log('All attributes are set, initializing socket');
        this.initializeSocket();
    } else {
        console.warn('Not all required attributes are set yet');
    }
  }

  setupElements() {
    // Centralizes element querying
    this.messagesArea = this.querySelector('.messages-area');
    this.questionInput = this.querySelector('.question-area textarea');
    this.sendButton = this.querySelector('.send-icon');
    this.languageSelect = this.querySelector('.language-select');
    this.statusLine = this.querySelector('.status-line');
    this.statusMessage = this.querySelector('.status-message');
    this.connectionStatusIcon = this.querySelector('.connection-status-icon');
  }

  addEventListeners() {
    // Centralized event listener setup
    this.sendButton.addEventListener('click', this.handleSendMessage);
    this.questionInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter' && !event.shiftKey) {
            event.preventDefault();
            this.handleSendMessage();
        }
    });
  }

  populateLanguageDropdown() {
    // Clear existing options
    this.languageSelect.innerHTML = '';
    console.log(`languages for options: ${this.languages}`)

    // Populate with new options
    this.languages.forEach(lang => {
      const option = document.createElement('option');
      option.value = lang;
      option.textContent = lang.toUpperCase();
      if (lang === this.currentLanguage) {
        option.selected = true;
      }
      console.log(`Adding option for language: ${lang}`)
      this.languageSelect.appendChild(option);
    });

    // Add event listener for language change
    this.languageSelect.addEventListener('change', (e) => {
      this.currentLanguage = e.target.value;
      // You might want to emit an event or update the backend about the language change
    });
  }

  attributeChangedCallback(name, oldValue, newValue) {
      console.log(`Attribute ${name} changed from ${oldValue} to ${newValue}`);

      // Handle token updates specially
      if (name === 'session-token' && oldValue !== newValue) {
          this.updateAttributes();
          if (newValue) {
              console.log('Received new session token');
              this.sessionToken = newValue;

              // If socket exists, reconnect with new token
              if (this.socket) {
                  this.socket.disconnect();
                  this.initializeSocket();
              } else if (this.areAllAttributesSet()) {
                  // Initialize socket if all other attributes are ready
                  this.initializeSocket();
              }
          }
          return;
      }

      if (name === 'languages' || name === 'language') {
        this.updateAttributes();
        this.populateLanguageDropdown();
        return;
      }

      this.updateAttributes();
  }

  updateAttributes() {
    this.tenantId = parseInt(this.getAttribute('tenant-id'));
    this.sessionToken = this.getAttribute('session-token');
    this.language = this.getAttribute('language');
    const languageAttr = this.getAttribute('languages');
    this.languages = languageAttr ? languageAttr.split(',') : [];
    this.serverUrl = this.getAttribute('server-url');
    this.currentLanguage = this.language;
    this.specialistId = this.getAttribute('specialist-id');
    console.log('Updated attributes:', {
      tenantId: this.tenantId,
      sessionToken: this.sessionToken,
      language: this.language,
      currentLanguage: this.currentLanguage,
      languages: this.languages,
      serverUrl: this.serverUrl,
      specialistId: this.specialistId
    });
  }

  areAllAttributesSet() {
    console.log('Checking if all attributes are set:', {
        tenantId: this.tenantId,
        sessionToken: this.sessionToken,
        language: this.language,
        languages: this.languages,
        serverUrl: this.serverUrl,
        specialistId: this.specialistId
    });

    const requiredAttributes = [
        'tenant-id',
        'session-token',
        'language',
        'languages',
        'specialist-id',
        'server-url'
    ];

    return requiredAttributes.every(attr => this.getAttribute(attr));
}

  handleTokenUpdate(newToken) {
    if (this.socket && this.socket.connected) {
        console.log('Updating socket connection with new token');
        // Emit token update event to server
        this.socket.emit('update_token', { token: newToken });
    } else if (newToken && !this.socket) {
        // If we have a new token but no socket, try to initialize
        this.initializeSocket();
    }
  }

  initializeSocket() {
    console.log(`Initializing socket connection to Evie at ${this.serverUrl}`);
    if (this.socket) {
      console.log('Socket already initialized');
      return;
    }

    if (!this.sessionToken) {
        console.error('Cannot initialize socket without session token');
        return;
    }

    this.socket = io(this.serverUrl, {
      path: '/chat/socket.io/',
      transports: ['websocket'],
      query: {  // Change from auth to query
        token: this.sessionToken
      },
      reconnectionAttempts: 5, // Infinite reconnection attempts
      reconnectionDelay: 5000,        // Delay between reconnections
      timeout: 20000,                 // Connection timeout
    });

    if (!this.socket) {
      console.error('Error initializing socket')
    } else {
      console.log('Socket initialized')
    }

    this.setupSocketEventHandlers();
  }

  setupSocketEventHandlers() {
    // connect handler --------------------------------------------------------
    this.socket.on('connect', (data) => {
      console.log('Socket connected OK');
      this.setStatusMessage('Connected to EveAI.');
      this.updateConnectionStatus(true);
      this.startHeartbeat();

      if (data?.room) {
        this.room = data.room;
        this.lastRoom = this.room;
        console.log(`Joined room: ${this.room}`);
      } else {
        console.log('Room information not received on connect');
      }
    });

    // authenticated handler --------------------------------------------------
    this.socket.on('authenticated', (data) => {
      console.log('Authenticated event received');
      this.setStatusMessage('Authenticated.');
      if (data?.room) {
        this.room = data.room;
        this.lastRoom = this.room;
        console.log(`Confirmed room: ${this.room}`);
      } else {
        console.log('Room information not received on authentication');
      }
    });

    // Room join handler ------------------------------------------------------
    this.socket.on('room_join', (data) => {
        console.log('Room join event received:', data);
        if (data?.room) {
            this.room = data.room;
            this.lastRoom = this.room;
            console.log(`Joined room: ${this.room}`);
        }
    });

    // connect-error handler --------------------------------------------------
    this.socket.on('connect_error', (err) => {
      console.error('Socket connection error:', err);
      this.setStatusMessage('Connection Error: EveAI Chat Widget needs further configuration by site administrator.');
      this.updateConnectionStatus(false);
    });

    // connect-timeout handler ------------------------------------------------
    this.socket.on('connect_timeout', () => {
      console.error('Socket connection timeout');
      this.setStatusMessage('Connection Timeout: EveAI Chat Widget needs further configuration by site administrator.');
      this.updateConnectionStatus(false);
    });

    // disconnect handler -----------------------------------------------------
    this.socket.on('disconnect', (reason) => {
      console.log('Socket disconnected: ', reason);
      if (reason === 'io server disconnect') {
        // Server disconnected the socket
        this.socket.connect(); // Attempt to reconnect
      }
      this.setStatusMessage('Disconnected from EveAI. Please refresh the page for further interaction.');
      this.updateConnectionStatus(false);
      this.stopHeartbeat();
      this.room = null;
    });

    // Token related handlers -------------------------------------------------
    this.socket.on('token_expired', () => {
      console.log('Token expired');
      this.setStatusMessage('Session expired. Please refresh the page.');
      this.updateConnectionStatus(false);
    });

    // reconnect_attempt handler ----------------------------------------------
    this.socket.on('reconnect_attempt', (attemptNumber) => {
      console.log(`Reconnection attempt ${attemptNumber}`);
      this.setStatusMessage(`Reconnecting... (Attempt ${attemptNumber})`);
      this.reconnectAttempts = attemptNumber;
    });

    // reconnect handler ------------------------------------------------------
    this.socket.on('reconnect', () => {
      console.log('Successfully reconnected to the server');
      this.setStatusMessage('Reconnected to EveAI.');
      this.updateConnectionStatus(true);
      this.startHeartbeat();
    });

    // reconnect failed -------------------------------------------------------
    this.socket.on('reconnect_failed', () => {
      console.log('Reconnection failed');
      this.setStatusMessage('Unable to reconnect. Please refresh the page.');
      this.handleReconnectFailure();
    });

    // room rejoin result -----------------------------------------------------
    this.socket.on('room_rejoin_result', (response) => {
      if (response.success) {
        console.log('Successfully rejoined room');
        this.room = response.room;
        this.setStatusMessage('Reconnected successfully.');
      } else {
        console.error('Failed to rejoin room');
        this.handleRoomRejoinFailure();
      }
    });

    // bot_response handler ---------------------------------------------------
    this.socket.on('bot_response', (data) => {
      console.log('Bot response received: ', data);
      if (data.tenantId === this.tenantId && data?.room === this.room) {
        setTimeout(() => this.startTaskCheck(data.taskId), 1000);
        this.setStatusMessage('Processing...');
      } else {
        console.log('Received message for different room or tenant, ignoring');
      }
    });

    // task_status handler ----------------------------------------------------
    this.socket.on('task_status', (data) => {
        console.log('Task status received:', data);
        if (!this.room) {
            console.log('No room assigned, cannot process task status');
            return;
        }
        this.handleTaskStatus(data);
    });

    // Feedback handler -------------------------------------------------------
    this.socket.on('feedback_received', (data) => {
        if (data?.room === this.room) {
            this.setStatusMessage(data.status === 'success' ? 'Feedback recorded.' : 'Failed to record feedback.');
        }
    });
  }

  attemptRoomRejoin() {
    console.log(`Attempting to rejoin room: ${this.lastRoom}`);
    this.socket.emit('rejoin_room', {
      token: this.sessionToken,
      tenantId: this.tenantId,
      previousRoom: this.lastRoom,
      timestamp: Date.now()
    });
  }

  handleReconnectFailure() {
    this.room = null;
    this.lastRoom = null;
    this.reconnectAttempts = 0;
    this.updateConnectionStatus(false);

    // Optionally reload the widget
    if (confirm('Connection lost. Would you like to refresh the chat?')) {
      window.location.reload();
    }
  }

  handleRoomRejoinFailure() {
    // Clear room state
    this.room = null;
    this.lastRoom = null;

    // Request new room
    this.socket.emit('request_new_room', {
      token: this.sessionToken,
      tenantId: this.tenantId
    });
  }

  clearRoomState() {
      // Use when intentionally leaving/clearing a room
      this.room = null;
      this.lastRoom = null;
      this.reconnectAttempts = 0;
    }

  handleAuthError(error) {
    console.error('Authentication error:', error);
    this.setStatusMessage('Authentication failed. Please refresh the page.');
    this.updateConnectionStatus(false);

    if (this.socket) {
        this.socket.disconnect();
    }
  }

  setStatusMessage(message) {
    this.statusMessage.textContent = message;
  }

  updateConnectionStatus(isConnected) {
    if (isConnected) {
      this.connectionStatusIcon.textContent = 'link';
      this.connectionStatusIcon.classList.remove('status-disconnected');
      this.connectionStatusIcon.classList.add('status-connected');
    } else {
      this.connectionStatusIcon.textContent = 'link_off';
      this.connectionStatusIcon.classList.remove('status-connected');
      this.connectionStatusIcon.classList.add('status-disconnected');
    }
  }

  startHeartbeat() {
      this.stopHeartbeat(); // Clear any existing interval
      this.heartbeatInterval = setInterval(() => {
        if (this.socket && this.socket.connected) {
          this.socket.emit('heartbeat');
          this.idleTime += 30000;
          if (this.idleTime >= this.maxConnectionIdleTime) {
              this.socket.disconnect();
              this.setStatusMessage('Disconnected due to inactivity.');
              this.updateConnectionStatus(false);
              this.stopHeartbeat();
          }
        }
      }, 30000); // Send a heartbeat every 30 seconds
    }

  stopHeartbeat() {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
  }

  updateProgress() {
    if (!this.statusMessage.textContent) {
        this.statusMessage.textContent = 'Processing...';
    } else {
        this.statusMessage.textContent += '.'; // Append a dot
    }
  }

  clearProgress() {
    this.statusMessage.textContent = '';
    this.toggleSendButton(false); // Re-enable and revert send button to outlined version
  }

  checkTaskStatus(taskId) {
    this.updateProgress();
    this.socket.emit('check_task_status', { task_id: taskId });
  }

  getTemplate() {
    return `
      <div class="chat-container">
        <div class="messages-area"></div>
        <div class="disclaimer">Evie can make mistakes. Please double-check responses.</div>
        <div class="question-area">
          <textarea placeholder="Type your message here..." rows="3"></textarea>
          <div class="right-side">
            <select class="language-select"></select>
            <i class="material-icons send-icon outlined">send</i>
          </div>
        </div>
        <div class="status-line">
            <i class="material-icons connection-status-icon">link_off</i>
            <span class="status-message"></span>
        </div>
      </div>
    `;
  }

  addUserMessage(text) {
    const message = document.createElement('div');
    message.classList.add('message', 'user');
    message.innerHTML = `<p>${text}</p>`;
    this.messagesArea.appendChild(message);
    this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
  }

  handleFeedback(feedback, interactionId) {
    // Send feedback to the backend
    console.log('handleFeedback called');
    if (!this.socket) {
      console.error('Socket is not initialized');
      return;
    }

    if (!this.validateRoom()) {
      console.log("No valid room to handle feedback")
      return;
    }

    console.log(`Sending feedback for ${interactionId}: ${feedback}`);
    this.socket.emit('feedback', { tenant_id: this.tenantId, token: this.sessionToken, feedback, interactionId, room: this.room });
    this.setStatusMessage('Feedback sent.');
  }

  addBotMessage(text, interactionId, algorithm = 'default', citations = []) {
    const message = document.createElement('div');
    message.classList.add('message', 'bot');

    let content = marked.parse(text);
    let citationsHtml = citations.map(url => `<a href="${url}" target="_blank">${url}</a>`).join('<br>');

    let algorithmClass;
    switch (algorithm) {
      case 'RAG_TENANT':
        algorithmClass = 'fingerprint-rag-tenant';
        break;
      case 'RAG_WIKIPEDIA':
        algorithmClass = 'fingerprint-rag-wikipedia';
        break;
      case 'RAG_GOOGLE':
        algorithmClass = 'fingerprint-rag-google';
        break;
      case 'LLM':
        algorithmClass = 'fingerprint-llm';
        break;
      default:
        algorithmClass = '';
    }

    message.innerHTML = `
      <p>${content}</p>
      ${citationsHtml ? `<p class="citations">${citationsHtml}</p>` : ''}
      <div class="message-icons">
          <i class="material-icons ${algorithmClass}">fingerprint</i>
          <i class="material-icons thumb-icon outlined" data-feedback="up" data-interaction-id="${interactionId}">thumb_up_off_alt</i>
          <i class="material-icons thumb-icon outlined" data-feedback="down" data-interaction-id="${interactionId}">thumb_down_off_alt</i>
      </div>
    `;
    this.messagesArea.appendChild(message);

    // Add event listeners for feedback buttons
    const thumbsUp = message.querySelector('i[data-feedback="up"]');
    const thumbsDown = message.querySelector('i[data-feedback="down"]');
    thumbsUp.addEventListener('click', () => this.toggleFeedback(thumbsUp, thumbsDown, 'up', interactionId));
    thumbsDown.addEventListener('click', () => this.toggleFeedback(thumbsUp, thumbsDown, 'down', interactionId));

    this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
  }

  toggleFeedback(thumbsUp, thumbsDown, feedback, interactionId) {
    console.log('feedback called');
    this.idleTime = 0; // Reset idle time
    if (feedback === 'up') {
      thumbsUp.textContent = 'thumb_up'; // Change to filled icon
      thumbsUp.classList.remove('outlined');
      thumbsUp.classList.add('filled');
      thumbsDown.textContent = 'thumb_down_off_alt'; // Keep the other icon outlined
      thumbsDown.classList.add('outlined');
      thumbsDown.classList.remove('filled');
    } else {
      thumbsDown.textContent = 'thumb_down'; // Change to filled icon
      thumbsDown.classList.remove('outlined');
      thumbsDown.classList.add('filled');
      thumbsUp.textContent = 'thumb_up_off_alt'; // Keep the other icon outlined
      thumbsUp.classList.add('outlined');
      thumbsUp.classList.remove('filled');
    }

    // Send feedback to the backend
    this.handleFeedback(feedback, interactionId);
  }

  handleSendMessage() {
    console.log('handleSendMessage called');
    this.idleTime = 0; // Reset idle time
    if (!this.socket?.connected) {
        console.error('Cannot send message: socket not connected');
        this.setStatusMessage('Not connected to server. Please try again.');
        return;
    }

    if (!this.room) {
        console.error('Cannot send message: no room assigned');
        this.setStatusMessage('Connection not ready. Please wait...');
        // Try to rejoin room if we have a last known room
        if (this.lastRoom) {
            this.attemptRoomRejoin();
        }
        return;
    }
    const message = this.questionInput.value.trim();
    if (message) {
      this.addUserMessage(message);
      this.questionInput.value = '';
      this.sendMessageToBackend(message);
      this.toggleSendButton(true); // Disable and change send button to filled version
    }
  }

  startTaskCheck(taskId) {
    if (!this.validateRoom()) {
      console.error('Cannot check task status: no room assigned');
      return;
    }
    console.log('Emitting check_task_status for:', taskId);
    this.socket.emit('check_task_status', {
        task_id: taskId,
        token: this.sessionToken,
        tenant_id: this.tenantId,
        room: this.room
    });
  }

  handleTaskStatus(data) {
      if (data.status === 'pending') {
          this.updateProgress();
          setTimeout(() => this.startTaskCheck(data.taskId), 1000);
      } else if (data.status === 'success') {
          if (data.results) {
              this.addBotMessage(
                  data.results.answer,
                  data.interaction_id,
                  'RAG_TENANT',
                  data.results.citations || []
              );
          } else {
              console.error('Missing results in task status response:', data);
          }
          this.clearProgress();
      } else {
          console.error('Task error:', data);
          this.setStatusMessage('Failed to process message.');
      }
  }
  sendMessageToBackend(message) {
    if (!this.socket || !this.room) {
        console.error('Cannot send message: socket or room not available');
        return;
    }

    if (!this.validateRoom()) {
      return;
    }

    const selectedLanguage = this.languageSelect.value;
    const messageData = {
        tenant_id: parseInt(this.tenantId),
        token: this.sessionToken,
        specialist_id: parseInt(this.specialistId),
        arguments: {
            language: selectedLanguage,
            query: message
        },
        timezone: this.userTimezone,
        room: this.room
    };

    console.log('Sending message to backend:', messageData);
    this.socket.emit('user_message', messageData);
    this.setStatusMessage('Processing started ...');
  }

  toggleSendButton(isProcessing) {
    if (isProcessing) {
      this.sendButton.textContent = 'send'; // Filled send icon
      this.sendButton.classList.remove('outlined');
      this.sendButton.classList.add('filled');
      this.sendButton.classList.add('disabled'); // Add disabled class for styling
      this.sendButton.style.pointerEvents = 'none'; // Disable click events
    } else {
      this.sendButton.textContent = 'send'; // Outlined send icon
      this.sendButton.classList.add('outlined');
      this.sendButton.classList.remove('filled');
      this.sendButton.classList.remove('disabled'); // Remove disabled class
      this.sendButton.style.pointerEvents = 'auto'; // Re-enable click events
    }
  }

  validateRoom() {
    if (!this.room) {
      console.error('No room assigned');
      this.setStatusMessage('Connection not ready. Please wait...');
      // Try to rejoin room if we have a last known room
      if (this.lastRoom) {
        this.attemptRoomRejoin();
      }
      return false;
    }
    return true;
  }
}

customElements.define('eveai-chat-widget', EveAIChatWidget);

