let socket = null;
let currentUserId;
let currentUsername;
let selectedUserId;
let currentAvatar;
let isAdmin = 0;
let offset = 0;
let chatLoading = false;
let menuVisible = false;  // keep delete message menu hidden
let hideTimer;  // declare the hide delete menu timer
let isMobile = false;
let allUsers = []; // Global list of users for forwarding etc.
let currentProjectRoomId = null;
let allTasksCache = []; // tasks filter cache

const chatMessages = document.getElementById('chatMessages');
const typingStatus = document.getElementById('typingStatus');
const notificationSound = new Audio('/sounds/notification.mp3');
const pendingMessages = {}; // Temporarily holds messages per user
const taskContainer = document.getElementById("taskList");
const loader = document.getElementById('loader');
const readByUsers = new Set(); // This stores user IDs who have read messages
let replyContext = null;

const offsetMinutes = -new Date().getTimezoneOffset(); // correct signed offset

let isEnterSendEnabled;
let playNotificationSound;
let showNotifications;
// get and apply the setrtings

// -----------------------------
// PROJECT ROOMS CONTEXT
// -----------------------------
let activeLeftTab = 'chat'; // 'chat' | 'rooms'

let chatContext = {
  mode: 'direct', // 'direct' | 'project-user' | 'project-room'
  projectId: null,
  projectName: null
};

// -----------------------------
// PROJECT UNREAD COUNTS
// -----------------------------
const projectUnreadCount = {}; // { [projectId]: number }

document.addEventListener('click', () => {
  if (menuVisible) {
    hideMenu();
  }
});

async function login() {
  const email = document.getElementById('loginEmail').value.trim();
  const password = document.getElementById('loginPassword').value;
  const captchaToken = grecaptcha.getResponse();

  if (!captchaToken) {
    document.getElementById('loginError').innerText = 'Please complete Captcha';
    return;
  }

  document.getElementById('loginError').innerText = '';

  try {
    const res = await fetch('/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password, captchaToken })
    });

    const data = await res.json();

    if (!data.success) {
      document.getElementById('loginError').innerText =
        data.error || 'Invalid login or captcha failed';
      grecaptcha.reset();
      return;
    }

    /* ----------------------------
       AUTH / USER CONTEXT
    ----------------------------- */
    authToken = data.token;
    window.currentSocketToken = data.token;
    localStorage.setItem('authToken', authToken); // optional

    const user = data.user;

    currentUserId = user.id;
    currentUsername = `${user.firstName} ${user.lastName}`.trim();
    currentAvatar = user.avatar || null;
    isAdmin = user.role === 'admin';

    /* ----------------------------
       UI TRANSITION
    ----------------------------- */
    document.getElementById('loginContainer').style.display = 'none';
    document.getElementById('chatContainer').style.display = 'flex';

    /* ----------------------------
       SOCKET CONNECT
    ----------------------------- */
    connectUser();
    await loadUsers();


    // temp checking 
    console.log('Logged in as:', {
      currentUserId,
      currentUsername,
      isAdmin,
      hasSocketToken: !!window.currentSocketToken
    });


    /* ----------------------------
       OPTIONAL / LEGACY CALLS
       (safe to keep, may 404)
    ----------------------------- */
    //loadSettings();
    registerFCMToken();

    grecaptcha.reset();

    // Legacy features – will be fixed later
    loadTasks();
    loadProjectsForTasks();
    loadProjectTree(currentUserId); // 17 Jan 2026 uncommented

  } catch (err) {
    console.error('Login failed:', err);
    document.getElementById('loginError').innerText = 'Network error';
    grecaptcha.reset();
  }
}


// add event listener to help detect a disconnect
window.addEventListener('online', () => {
  console.log('Network online detected. Forcing socket reconnect...');
  setDisConnected();

  if (!socket || !socket.connected) {
    connectUser();   // 🔑 JWT already known
    loader.style.display = 'none';
    setConnected();
  }
});


let socketEventsBound = false;

function connectUser() {
  // Prevent duplicate sockets
  if (socket && socket.connected) {
    console.log('CLIENT socket already connected:', socket.id);
    return socket;
  }

  console.log('CLIENT creating socket');

  socket = io({
    auth: {
      token: window.currentSocketToken
    },
    transports: ['polling', 'websocket'],
    reconnection: true,
    reconnectionAttempts: Infinity,
    reconnectionDelay: 5000,
    timeout: 15000
  });

  bindSocketEvents(socket);

  return socket;
}

function bindSocketEvents(socket) {
  socket.on('connect', () => {
    if (chatContext?.projectId) {
      joinProjectRoom(chatContext.projectId);
    }
    updateStatus("Connected");
    loader.style.display = 'none';
  });

  socket.on("disconnect", (reason) => {
    updateStatus("Disconnected. Reconnecting...");
    loader.style.display = 'block';
    console.warn("Socket disconnected:", reason);
  });

  socket.on("reconnect_attempt", (attempt) => {
    updateStatus(`Reconnecting... Attempt #${attempt}`);
    loader.style.display = 'block';
  });

  socket.on("reconnect", () => {
    updateStatus("Connected");
    loader.style.display = 'none';
  });

  socket.on("reconnect_error", (error) => {
    updateStatus("Reconnection failed. Retrying...");
    loader.style.display = 'block';
    console.error("Reconnection error:", error);
  });

  socket.on("reconnect_failed", () => {
    updateStatus("Failed to reconnect.");
    loader.style.display = 'block';
  });

  // whenever a user joins or leaves, update his state in userList
  socket.on('presence_changed', () => {
    loadUsers();
  });

  /* ---------------------------
    AVATAR UPDATED
  ---------------------------- */
  socket.on('avatar_updated', ({ userId, avatar }) => {
    updateAvatar(userId, avatar);
  });

  /* ---------------------------
    TASKS / PROJECT FILES
  ---------------------------- */
  socket.on('task_updated', () => {
    loadTasks();
  });

  socket.on('proj_file_uploaded', ({ finalFilename, uploaderName, uploaderId }) => {
    loadProjectTree(currentUserId);

    const fileMsg = `${uploaderName} uploaded ${finalFilename}`;
    showToast(null, uploaderName, fileMsg);

    const direction = (uploaderId == currentUserId) ? 'sent' : 'received';

    appendMessage({
      message: fileMsg,
      senderId: uploaderId,
      receiverId: currentUserId,
      timestamp: new Date().toISOString(),
      isFile: false,
      source: 'system'
    }, direction);
  });

  /* ---------------------------
     RECEIVE MESSAGE
  ---------------------------- */
  socket.on('receive_message', (msg) => {
    /* -----------------------------
       FILE MESSAGE NORMALIZATION
    ----------------------------- */
    if (msg.is_file === 1 || msg.isFile === true) {
      msg.isFile = true;

      if (!msg.fileUrl && msg.message && msg.message.startsWith('http')) {
        msg.fileUrl = msg.message;
        msg.message = '';
      }
    }

    /* -----------------------------
       PROJECT UNREAD TRACKING
    ----------------------------- */
    if (msg.projectId) {
      const isActiveProject =
        chatContext?.projectId === msg.projectId &&
        (
          chatContext.mode === 'project-room' ||
          chatContext.mode === 'project-user'
        );

      if (!isActiveProject) {
        projectUnreadCount[msg.projectId] =
          (projectUnreadCount[msg.projectId] || 0) + 1;

        const projectHeaders = document.querySelectorAll('.project-node');
        projectHeaders.forEach(header => {
          const badge = getOrCreateProjectBadge(msg.projectId, header);
          badge.textContent = projectUnreadCount[msg.projectId];
          badge.style.display = 'inline-block';
        });
      }
    }

    /* =====================================================
       🔑 PROJECT ROOM LIVE DISPLAY (FIX)
    ===================================================== */
    if (msg.projectId && chatContext?.mode === 'project-room') {
      if (msg.projectId !== chatContext.projectId) return;

      appendMessage(msg);
      scrollToBottom();
      return;
    }

    /* =====================================================
       🔑 DIRECT / PROJECT-USER CHAT (UNCHANGED)
    ===================================================== */

    // HARD FILTER for direct messages only
    if (
      msg.senderId !== currentUserId &&
      msg.receiverId !== currentUserId
    ) {
      return;
    }

    const isSentByCurrent = msg.senderId === currentUserId;
    const otherUserId = isSentByCurrent ? msg.receiverId : msg.senderId;
    const isChatOpen = Number(selectedUserId) === Number(otherUserId);

    if (isChatOpen) {
      appendMessage(msg);
      scrollToBottom();

      if (!isSentByCurrent) {
        socket.emit('message_read', {
          senderId: otherUserId,
          receiverId: currentUserId
        });
      }
    } else {
      if (!pendingMessages[otherUserId]) {
        pendingMessages[otherUserId] = [];
      }

      pendingMessages[otherUserId].push(msg);

      if (!isSentByCurrent) {
        showToast(
          msg.senderId,
          msg.senderName || 'User',
          msg.message
        );

        const userDiv = document.querySelector(
          `.user-item[data-userid="${msg.senderId}"]`
        );

        if (userDiv && !userDiv.classList.contains('blink')) {
          userDiv.classList.add('blink');
        }
      }
    }

    if (playNotificationSound && !isSentByCurrent) {
      notificationSound.play();
    }
  });



  /* ---------------------------
     MESSAGE DELETED
  ---------------------------- */
  socket.on('message_deleted', ({ messageId }) => {
    if (!messageId) return;

    const bubble = document.querySelector(
      `.message[data-message-id="${messageId}"]`
    );

    if (bubble) {
      bubble.remove();
    }
  });


  /* ---------------------------
     TYPING INDICATORS
  ---------------------------- */
  socket.on('typing', (data) => {
    if (!data) return;

    const { senderId, receiverId, senderName } = data;

    // 🔒 Ignore if not meant for me
    if (receiverId !== currentUserId) return;

    // Only show typing if this chat is open
    if (Number(senderId) === Number(selectedUserId)) {
      const safeName = document.createTextNode(
        senderName || 'User'
      ).textContent;

      typingStatus.innerHTML =
        `${safeName} is typing<span class="dots">.</span>`;
    }
  });


  socket.on('stop_typing', (data) => {
    if (!data) {
      typingStatus.textContent = '';
      return;
    }

    const { senderId, receiverId } = data;

    // 🔒 Ignore if not meant for me
    if (receiverId !== currentUserId) return;

    // Clear only if it matches active chat
    if (Number(senderId) === Number(selectedUserId)) {
      typingStatus.textContent = '';
    }
  });


  /* ---------------------------
     CHAT HISTORY
  ---------------------------- */

  /* ---------------------------
     RECEIVE FILE
  ---------------------------- */
  socket.on('receive_file', (fileData) => {
    // normalize payload
    fileData.senderId = fileData.senderId ?? fileData.sender_id;
    fileData.receiverId = fileData.receiverId ?? fileData.receiver_id;
    fileData.isFile = true;

    const isSender = Number(fileData.senderId) === currentUserId;
    const otherUserId = isSender ? fileData.receiverId : fileData.senderId;
    const isChatOpen = Number(selectedUserId) === Number(otherUserId);

    if (isChatOpen) {
      appendMessage(fileData);
      scrollToBottom();
    } else {
      if (!pendingMessages[otherUserId]) pendingMessages[otherUserId] = [];
      pendingMessages[otherUserId].push(fileData);

      if (!isSender) {
        showToast(
          fileData.senderId,
          fileData.senderName || 'User',
          'Sent a file'
        );
        if (playNotificationSound) notificationSound.play();
      }
    }
  });
}


async function loadUsers() {
  try {
    if (!authToken) {
      console.warn('No authToken, cannot load users');
      return;
    }

    const res = await fetch('/users', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${authToken}`,
        'Content-Type': 'application/json'
      }
    });

    if (!res.ok) {
      throw new Error(`Failed to load users: ${res.status}`);
    }

    const users = await res.json();
    allUsers = users; // cache for project rooms

    // 🔹 This is your EXISTING renderer
    renderUsers(users);
    renderWelcomeBox(currentUserId);

  } catch (err) {
    console.error('Error loading user list:', err);
  }
}

/* ---------------------------
   TYPING EMIT (SAFE)
---------------------------- */
document.getElementById('chatInput').addEventListener('input', () => {
  if (!selectedUserId) return;

  socket.emit('typing', {
    senderId: currentUserId,
    receiverId: selectedUserId,
    senderName: currentUsername
  });

  clearTimeout(window.typingTimeout);
  window.typingTimeout = setTimeout(() => {
    socket.emit('stop_typing', {
      senderId: currentUserId,
      receiverId: selectedUserId
    });
  }, 800);
});

/* ---------------------------
   LOAD MORE HISTORY (SAFE)
---------------------------- */
chatMessages.addEventListener('scroll', () => {
  if (chatMessages.scrollTop < 10 && !chatLoading && selectedUserId) {
    chatLoading = true;
    socket.emit('load_history', {
      senderId: currentUserId,
      receiverId: selectedUserId,
      offset
    });
  }
});

/* ---------------------------
   OPEN CHAT
---------------------------- */
function openChatWith(userId, username) {
  selectedUserId = userId;
  offset = 0;
  chatLoading = false;
  typingStatus.textContent = '';

  chatMessages.innerHTML = '';
  //must have event to load chat 
  // ✅ Set chat context (direct user chat)
  chatContext = { mode: 'direct' };

  // ✅ Load chat history via HTTPS
  reloadCurrentChatHistory(true);

  document.getElementById('buttonsContainer').style.display = 'flex';
  document.getElementById('sub-bar').style.display = 'flex';
  document.getElementById('whoIs').style.display = 'flex';
}


//wrap in code tags
function wrapMessage() {
  const input = document.getElementById('chatInput');
  if (!input) return;

  const start = input.selectionStart;
  const end = input.selectionEnd;

  const text = input.value;
  const selected = text.substring(start, end).trim();

  const content = selected || text.trim();
  if (!content) return;

  // prevent double wrapping
  if (content.startsWith('<code>') && content.endsWith('</code>')) {
    return;
  }

  const wrapped = `<code>\n${content}\n</code>`;

  if (selected) {
    input.value =
      text.substring(0, start) + wrapped + text.substring(end);
    input.selectionStart = start;
    input.selectionEnd = start + wrapped.length;
  } else {
    input.value = wrapped;
    input.selectionStart = 0;
    input.selectionEnd = wrapped.length;
  }

  input.focus();
}


function sendMessage() {
  const input = document.getElementById('chatInput');
  const msg = input.value.trim();

  console.log('CLIENT sendMessage socket state:', {
    socketExists: !!socket,
    connected: socket?.connected,
    id: socket?.id,
    mode: chatContext?.mode
  });

  if (!msg) return;
  if (!socket || !socket.connected) return;

  const payload = {
    message: msg,
    tempId: Date.now()
  };

  // 🔑 PROJECT ROOM
  if (chatContext?.mode === 'project-room') {
    payload.mode = 'project-room';
    payload.projectId = chatContext.projectId;
    payload.roomId = chatContext.roomId ?? null;
  }

  // 🔑 USER / PROJECT-USER CHAT
  else {
    if (!selectedUserId) return;

    payload.receiverId = selectedUserId;
  }

  socket.emit('send_message', payload);

  input.value = '';
}


///APPEND MESSAGE

function appendMessage(msg, prepend = false) {

  // 🔑 Normalize senderId (history vs live)
  if (msg.senderId == null && msg.sender_id != null) {
    msg.senderId = msg.sender_id;
  }
  const isSentByMe = Number(msg.senderId) === Number(currentUserId);
  const type = isSentByMe ? 'sent' : 'received';

  const bubble = document.createElement('div');
  bubble.className = `message ${type}`;
  bubble.dataset.messageId = msg.id;
  bubble.dataset.source = msg.source;

  let content = '';

  // Render reply snippet if this is a reply message
  if (msg.replyTo) {
    const replySender = escapeHTML(msg.replyTo.senderName);
    const replySnippet = escapeHTML(msg.replyTo.snippet);
    const replyIcon = msg.replyTo.isFile ? '<i class="fa fa-paperclip"></i> ' : '';

    content += `
    <div class="reply-block" style="
      border-left: 3px solid #ccc;
      padding-left: 8px;
      margin-bottom: 5px;
      font-size: 0.9em;
      color: #555;
    ">
      <strong>${replySender}:</strong><br>
      ${replyIcon}${replySnippet}
    </div>
  `;
  }

  // Determine file URL if message is a file
  const fileUrl = msg.fileUrl || null;

  const fileName = msg.filename || (fileUrl ? fileUrl.split('/').pop() : '');

  if (msg.isFile && fileUrl) {
    const ext = fileName.split('.').pop().toLowerCase();
    if (['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext)) {
      content += `
        <ion-icon name="image-outline"></ion-icon>
        <a href="${fileUrl}" target="_blank">
        <img src="${escapeAttr(fileUrl)}" class="file-preview" alt="preview">
        </a>`;

    } else {
      let icon = '<ion-icon name="document-outline"></ion-icon>'; // default

      if (ext === 'pdf') icon = '<i class="fa-regular fa-file-pdf"></i>';
      else if (['doc', 'docx'].includes(ext)) icon = '<i class="fa-regular fa-file-word"></i>';
      else if (['xls', 'xlsx'].includes(ext)) icon = '<i class="fa-regular fa-file-excel"></i>';
      else if (['zip', 'rar', '7z'].includes(ext)) icon = '<i class="fa-solid fa-file-zip"></i>';
      else if (['mp3', 'wav'].includes(ext)) icon = '<i class="fa-regular fa-file-audio"></i>';
      else if (['mp4', 'webm'].includes(ext)) icon = '<i class="fa-regular fa-file-video"></i>';

      content += `
    ${icon}
    <a href="${escapeAttr(fileUrl)}" target="_blank" class="file-link">${escapeHTML(fileName)}</a>`
    }
  } else if (msg.message) {
    if (msg.isCode) {
      let rawMessage = msg.message.trim();

      // Remove literal <code>...</code> if present
      if (
        rawMessage.startsWith('<code>') &&
        rawMessage.endsWith('</code>')
      ) {
        rawMessage = rawMessage
          .replace(/^<code>/, '')
          .replace(/<\/code>$/, '');
      }

      // Ensure unescaped text — avoid double escaping
      const copyId = 'copy-' + Date.now() + '-' + Math.floor(Math.random() * 1000);
      const result = hljs.highlightAuto(rawMessage);
      const detectedLang = result.language || 'plaintext';
      const highlightedHTML = result.value;

      content += `
      <div style="position: relative; max-width: 600px;">
        <button 
          onclick="copyCodeToClipboard('${copyId}')" 
          style="
            position: absolute;
            top: 5px;
            right: 5px;
            font-size: 12px;
            padding: 2px 6px;
            border: none;
            background: #333;
            color: #fff;
            border-radius: 3px;
            cursor: pointer;
            display:none;
          ">
          <i class="fa-solid fa-copy"></i> Copy 
        </button>
        <pre><code id="${copyId}" class="hljs language-${detectedLang}">${highlightedHTML}</code></pre>
      </div>`;
    } else {
      // Safe display for plain or lightly formatted HTML
      const rawEscaped = escapeHTML(msg.message);
      const withBreaks = rawEscaped.replace(/\n/g, '<br>');
      content += parseLinks(withBreaks);
      // const escaped = escapeHTML(msg.message).replace(/\n/g, '<br>');
      // content += parseLinks(escaped);
    }
  }

  // Prepare read icon for sent messages
  let readIcon = '';
  if (type === 'sent' && msg.isRead) {
    readIcon = ' <span class="readIcon"><ion-icon name="checkmark-done-outline"></ion-icon></span>';
  }

  //attach the share/forward button
  let replyIconHTML = '';

  if (type === 'received') {
    replyIconHTML = `<i class="fa fa-reply reply-icon" data-tooltip="Reply"></i>`;
  }

  let forwardIconHTML = `<i class="fa-solid fa-share forward-icon" data-tooltip="Forward""></i>`;
  const copyIconHTML = `<i class="fa-regular fa-copy copy-icon" data-tooltip="Copy"></i>`;

  // ==============================
  // BUILD DELETE ICON
  // ==============================

  const ellipsis = document.createElement('div');
  ellipsis.className = 'ellipsis';

  if (type === 'sent') {
    const faIcon = document.createElement('i');
    faIcon.className = 'fa-regular fa-trash-can delete-icon';

    ellipsis.appendChild(faIcon);

    faIcon.addEventListener('click', (e) => {
      e.stopPropagation();

      socket.emit('delete_message', {
        messageId: bubble.dataset.messageId,
        filename: msg.isFile ? fileName : null
      });
    });
  }


  // ==============================
  // META HTML (UNCHANGED)
  // ==============================

  const metaHTML = `
  <div class="meta">
    ${replyIconHTML}
    ${copyIconHTML}
    ${formatTimeAMPM(msg.timestamp)}${readIcon}
    ${forwardIconHTML}
  </div>
`;


  // ==============================
  // BUILD INNER BUBBLE
  // ==============================

  const inner = document.createElement('div');
  inner.className = 'bubble';

  inner.innerHTML = `
  <div class="message-content">
    ${content}
  </div>
`;


  // ==============================
  // CREATE TOOLBAR WRAPPER (NEW)
  // ==============================

  const toolbar = document.createElement('div');
  toolbar.className = 'toolbar';


  // Insert meta as DOM node (keeps selectors working)
  const metaWrapper = document.createElement('div');
  metaWrapper.innerHTML = metaHTML;
  const metaDiv = metaWrapper.firstElementChild;


  // ==============================
  // ASSEMBLE TOOLBAR
  // ==============================

  toolbar.appendChild(ellipsis);
  toolbar.appendChild(metaDiv);


  // ==============================
  // APPEND TOOLBAR TO INNER
  // ==============================

  inner.appendChild(toolbar);


  // ==============================
  // EXISTING ICON EVENT BINDINGS (UNCHANGED)
  // ==============================

  // Reply icon
  if (type === 'received') {
    const replyIcon = inner.querySelector('.reply-icon');
    if (replyIcon) {
      replyIcon.addEventListener('click', (e) => {
        e.stopPropagation();

        setReplyContext({
          id: msg.id,
          senderName: msg.senderName,
          message: msg.message,
          filename: msg.filename,
          isFile: msg.isFile,
        });

        document.getElementById('chatInput').focus();
      });
    }
  }


  // Copy icon
  const copyIcon = inner.querySelector('.copy-icon');
  if (copyIcon) {
    copyIcon.addEventListener('click', (e) => {
      e.stopPropagation();
      copyMsgtoClipboard(inner);
    });
  }


  // Forward icon
  const forwardIcon = inner.querySelector('.forward-icon');
  if (forwardIcon) {
    forwardIcon.addEventListener('click', (e) => {
      e.stopPropagation();
      forwardMessage(msg);
    });
  }


  // ==============================
  // FINAL DOM INSERT
  // ==============================

  bubble.style.position = 'relative';
  bubble.appendChild(inner);

  if (prepend) {
    chatMessages.prepend(bubble);
  } else {
    chatMessages.appendChild(bubble);
    chatMessages.scrollTop = chatMessages.scrollHeight;
  }

  notifyTabBlink("New Message!");
}

///////end apppend message////////

function hideMenu() {
  document.querySelectorAll('.contextMenu').forEach(menu => {
    menu.style.display = 'none';
  });
  menuVisible = false;
  clearTimeout(hideTimer);
}


//// end append message

//copy message function

function copyMsgtoClipboard(innerDiv) {
  // Clone the inner div and remove the meta div
  const clone = innerDiv.cloneNode(true);
  const meta = clone.querySelector('.meta');
  if (meta) meta.remove();

  // Get only the text content (e.g., the message)
  const textToCopy = clone.textContent.trim();

  navigator.clipboard.writeText(textToCopy).then(() => {
    console.log('Copied:', textToCopy);
    showToast(null, "Copied", "Message copied to clipboard");
  }).catch((err) => {
    console.error('Copy failed:', err);
  });
}


function renderUsers(users) {
  const list = document.getElementById('userList');
  list.innerHTML = '';

  const pastelColors = [
    '#FF70A6', // light pink
    '#CE9FF70', // light green
    '#70DEFF', // light blue
    '#FFD670', // light yellow
    '#FF9770', // light orange
    '#C0B0EC', // light purple
    '#7EC4CF'  // light cyan
  ];

  users.forEach(user => {
    if (user.id !== currentUserId) {
      const div = document.createElement('div');
      div.className = 'user-item';
      div.dataset.userid = user.id;

      let avatarHtml = '';
      if (user.avatar) {
        const avatarUrl = user.avatar.startsWith('http') ? user.avatar : `/${user.avatar}`;
        avatarHtml = `<img src="${avatarUrl}" class="avatar pic">`;
      } else {
        const fullName = user.firstName + " " + user.lastName;
        const initials = getInitials(fullName);
        const color = pastelColors[user.id % pastelColors.length];
        avatarHtml = `<div class="avatar initials" style="background-color: ${color};">${initials}</div>`;
      }

      div.innerHTML = `
  		${avatarHtml}
  		<span class="username">${user.firstName + " " + user.lastName}</span>
  		<span class="status-indicator ${user.is_online ? 'online' : 'offline'}"></span>
		`;

      div.onclick = () => {
        selectedUserId = user.id;
        document.getElementById('chatMessages').innerHTML = '';
        updateChatHeader(user.username);

        // need event to load chat history
        chatContext = { mode: 'direct' };
        reloadCurrentChatHistory(true);

        document.getElementById('buttonsContainer').style.display = 'flex';
        div.classList.remove('blink');

        document.querySelectorAll('.user-item.active').forEach(el => {
          el.classList.remove('active');
        });
        div.classList.add('active');

        if (isMobile == true) { //switch to chat tab
          updatecarousel(1);
        }

        if (selectedUserId === user.id) {
          markMessagesAsRead(user.id);
        }


      };

      list.appendChild(div);
    }
  });
  // Simulate click on the first user in the list
  // ✅ Auto-load first user chat on initial login
  if (!selectedUserId) {
    const firstUserItem = list.querySelector('.user-item');
    if (firstUserItem) {
      firstUserItem.click();
    }
  }
}

//update another user's avatar selectively when that user updates it
function updateAvatar(userId, newAvatarPath) {
  const userDiv = document.querySelector(`.user-item[data-userid="${userId}"]`);
  if (!userDiv || !newAvatarPath) return;

  const avatarContainer = userDiv.querySelector('.avatar');
  if (!avatarContainer) return;

  const avatarUrl = newAvatarPath.startsWith('http') ? newAvatarPath : `/${newAvatarPath}`;

  // Replace the entire avatar element
  avatarContainer.outerHTML = `<img src="${avatarUrl}" class="avatar">`;
}


function getInitials(name) {

  if (!name || typeof name !== 'string') {
    return 'U';
  }

  return name
    .trim()
    .split(/\s+/)
    .filter(Boolean)
    .map(part => part[0])
    .join('')
    .toUpperCase();
}


async function loadUserChatHistory(reset = false) {
  if (!selectedUserId) return;

  if (reset) {
    chatMessages.innerHTML = '';
    chatCursor = null;
  }

  const url = new URL('/api/chat/history', window.location.origin);
  url.searchParams.set('otherUserId', selectedUserId);
  if (chatCursor) url.searchParams.set('before', chatCursor);

  const res = await fetch(url.toString(), {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${authToken}`, // 🔑 REQUIRED
      'Accept': 'application/json'
    }
  });

  if (!res.ok) {
    console.error('User chat history failed:', res.status);
    return;
  }

  const data = await res.json();
  if (!data.items) return;

  data.items.forEach(msg => appendMessage(msg));
  chatCursor = data.nextCursor;
}



function updateChatHeader(name) {
  //console.log('Updating chat header:', name);
  const header = document.getElementById('chatHeader');
  const whois = document.getElementById('whoIs')
  if (header) {
    header.innerText = `Chatting with ${name}`;
    whois.innerText = `Chatting with ${name}`;
    whois.style.display = 'flex';
  } else {
    console.error('Chat header element not found');
  }
}

document.getElementById('fileInput').addEventListener('change', function () {
  const file = this.files[0];
  if (!file || !selectedUserId) return;

  /* -------------------------
     CLIENT-SIDE SAFEGUARDS
  -------------------------- */
  const MAX_SIZE = 10 * 1024 * 1024; // 10 MB
  const ALLOWED_TYPES = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'application/pdf',
    'text/plain',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  ];

  if (file.size > MAX_SIZE) {
    alert('File too large (max 10MB)');
    return;
  }

  if (!ALLOWED_TYPES.includes(file.type)) {
    alert('File type not allowed');
    return;
  }

  const token = localStorage.getItem('authToken');
  if (!token) {
    alert('Not authenticated');
    return;
  }

  const formData = new FormData();
  formData.append('file', file);
  formData.append('receiverId', selectedUserId); // ✅ intent only

  const xhr = new XMLHttpRequest();

  /* -------------------------
     PROGRESS UI
  -------------------------- */
  const progressBar = document.getElementById('uploadProgress');
  const progressContainer = document.getElementById('uploadProgressContainer');
  const percentText = document.getElementById('uploadPercent');

  progressContainer.style.display = 'flex';
  progressBar.value = 0;
  percentText.textContent = '0%';

  xhr.upload.addEventListener('progress', function (e) {
    if (e.lengthComputable) {
      const percent = Math.round((e.loaded / e.total) * 100);
      progressBar.value = percent;
      percentText.textContent = percent + '%';
    }
  });

  xhr.onload = function () {
    progressContainer.style.display = 'none';

    if (xhr.status === 200) {
      const result = JSON.parse(xhr.responseText);
      if (!result.success) {
        alert(result.message || 'Upload failed');
      }
    } else {
      alert('Upload failed (HTTP ' + xhr.status + ')');
    }
  };

  xhr.onerror = function () {
    progressContainer.style.display = 'none';
    alert('Upload network error');
  };

  xhr.open('POST', '/upload');
  xhr.setRequestHeader('Authorization', `Bearer ${token}`); // 🔐 JWT
  xhr.send(formData);
});



function showToast(fromUserId, fromUsername, message) {
  const toastContainer = document.getElementById('toastContainer');

  const toast = document.createElement('div');
  toast.className = 'toast';

  toast.innerHTML = `
    <span><strong>${fromUsername}:</strong> ${message}</span>
    <span class="close-toast">&times;</span>
  `;

  // Click opens the chat with sender
  toast.addEventListener('click', (e) => {
    if (!e.target.classList.contains('close-toast')) {
      openChatWith(fromUserId, fromUsername);
    }
  });

  // Close button
  toast.querySelector('.close-toast').addEventListener('click', () => {
    toast.remove();
  });

  toastContainer.appendChild(toast);

  // Auto-dismiss after 8 seconds
  setTimeout(() => {
    toast.remove();
  }, 5000);
}

function scrollToBottom() {
  const chatMessages = document.getElementById('chatMessages');
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

function renderWelcomeBox(user) {
  const welcomeBox = document.getElementById('welcome');
  //console.log (user.avatar);
  //console.log (currentAvatar);
  if (currentAvatar) {
    // If avatar exists, show avatar image
    welcomeBox.innerHTML = `
      <img src="${currentAvatar}" style="width:40px;height:40px;border-radius:50%;vertical-align:middle;margin-right:8px;" />
      <span class="selfName">${currentUsername}</span>
    `;
  } else {
    // If no avatar, fallback to initials with pastel background
    const initials = currentUsername.slice(0, 2).toUpperCase();
    const bgColors = ['#FADADD', '#DEF1F0', '#FFF5BA', '#D1E8E4', '#E0BBE4', '#FFDAC1', '#B5EAD7'];
    const color = bgColors[currentUsername.charCodeAt(0) % bgColors.length];

    welcomeBox.innerHTML = `
      <div class="selfAvatar" id="self">${initials}</div>
      <div class="selfName">Welcome ${currentUsername}</div>
    `;
    const selfDiv = document.getElementById('self');
    selfDiv.style.backgroundColor = color;
  }
}

////////////////////////////////////////////////
//// chat input resize and other customizations
////////////////////////////////////////////////

const chatInput = document.getElementById('chatInput');
const baseHeight = 45; // px for 2 lines or less
const maxHeight = 120; // px cap
const lineHeight = 20; // must match CSS

function getVisualLineCount() {
  const style = window.getComputedStyle(chatInput);
  const lineHeight = parseFloat(style.lineHeight);

  return Math.floor(chatInput.scrollHeight / lineHeight);
}

chatInput.addEventListener('input', adjustChatInputHeight);

function adjustChatInputHeight() {
  chatInput.style.height = `${baseHeight}px`;
  const visualLines = getVisualLineCount();

  if (visualLines <= 2 || chatInput.value.trim() === '') {
    chatInput.style.height = `${baseHeight}px`;
  } else {
    const newHeight = Math.min(baseHeight + (visualLines - 2) * lineHeight, maxHeight);
    chatInput.style.height = `${newHeight}px`;
  }
}

chatInput.addEventListener('keydown', function (event) {
  // ⌘/Ctrl+F = open custom search
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {
    event.preventDefault(); // stop browser default
    if (!searchWrapper.classList.contains('expanded')) {
      toggleSearchPanel();
    } else {
      searchInput.focus();
    }
    return; // don't continue further
  }

  // Enter without Shift = send
  if (event.key === 'Enter' && !event.shiftKey) {
    event.preventDefault();
    sendMessage();
    chatInput.value = '';
    chatInput.style.height = `${baseHeight}px`;
  }

  // Shift+Enter = allow newline (default behavior)
});



//for inactive windows receiving files and other messages
const messagesPerUser = {}; // e.g. { '2': [...msgs], '3': [...msgs] }

function storeMessage(msg) {
  const userId = msg.senderId === currentUserId ? msg.receiverId : msg.senderId;
  if (!messagesPerUser[userId]) messagesPerUser[userId] = [];
  messagesPerUser[userId].push(msg);
}

function insertDateSeparator(date) {
  const separator = document.createElement('div');
  separator.className = 'date-separator';

  const today = new Date();
  const dateStr = date.toDateString();
  const todayStr = today.toDateString();

  const label = dateStr === todayStr ? 'Today' : date.toLocaleDateString(undefined, {
    weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
  });

  separator.innerHTML = `<div class="separator-line">${label}</div>`;
  chatMessages.prepend(separator);
}

function markMessagesAsRead(senderId) {
  if (!window.socket) {
    return;
  }

  socket.emit('message_read', {
    senderId,
    receiverId: currentUserId
  });
}



////////////////////////////////
////TASKS LIST ITEMS HERE
///////////////////////////////

function loadTasks() {
  const refreshIcon = document.getElementById('refreshIcon');
  refreshIcon.classList.add('rotate'); // Start rotation
  const token = localStorage.getItem('authToken');
  if (!token) return;

  fetch('/tasks', {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  })
    .then(res => {
      if (!res.ok) {
        return res.json().then(d => {
          throw new Error(d.message || 'Failed to load tasks');
        });
      }
      return res.json();
    })
    .then(data => {
      allTasksCache = data.tasks;
      populateTaskProjectFilter(allTasksCache);
      applyTaskFilter();

    })
    .catch(err => {
      console.error('Load tasks error:', err);
    });
      refreshIcon.classList.remove('rotate'); // Stop rotation 
}

function renderTasks(tasks) {

  const container = document.getElementById('taskList');
  if (!container) return;

  container.innerHTML = '';

  if (!tasks || tasks.length === 0) {
    container.innerHTML = '<p>No tasks</p>';
    return;
  }

  tasks.forEach(task => {

    const taskDiv = document.createElement('div');
    taskDiv.className = 'taskOuter';


    // -------------------------
    // Project / Personal Label
    // -------------------------

    let taskSourceLabel = 'Personal';

    if (task.projectName) {
      taskSourceLabel = `${task.projectName} - ${task.assignedByName || 'System'}`;
    }


    taskDiv.innerHTML = `
      <div class="taskItem">

        <div class="taskInner">
          <input type="checkbox" 
            class="chkTasks"
            ${task.isCompleted ? 'checked' : ''}
            onchange="toggleTask(${task.taskID}, this.checked)">

          <span class="descTasks">${task.description}</span>
        </div>

        <button class="btnDelTask" onclick="deleteTask(${task.taskID})">
          <i class="fa fa-window-close" aria-hidden="true"></i>
        </button>

      </div>

      <div class="taskMetaRow">

        <div class="taskProjectInfo">
          ${taskSourceLabel}
        </div>

        <div class="taskDate">
          ${new Date(task.dateAdded).toLocaleDateString()}
        </div>

      </div>
    `;

    container.appendChild(taskDiv);
  });
}

function populateTaskProjectFilter(tasks) {

  const select = document.getElementById('taskProjectFilter');
  if (!select) return;

  select.innerHTML = '';

  // Static options
  select.appendChild(new Option('All Projects', 'all'));
  select.appendChild(new Option('Personal', 'personal'));

  const projectMap = new Map();

  tasks.forEach(task => {

    // Only real project tasks
    if (
      task.projectId &&
      task.projectName &&
      task.projectName.trim() !== ''
    ) {
      projectMap.set(task.projectId, task.projectName);
    }

  });

  projectMap.forEach((name, id) => {
    select.appendChild(new Option(name, id));
  });

  // Restore sticky value safely
  const saved = localStorage.getItem('taskProjectFilter');
  if (saved && [...select.options].some(o => o.value === saved)) {
    select.value = saved;
  }
}


function applyTaskFilter() {

  const select = document.getElementById('taskProjectFilter');
  const filter = select.value;

  localStorage.setItem('taskProjectFilter', filter);

  let filteredTasks = [];

  // ----------------------
  // PERSONAL
  // ----------------------
  if (filter === 'personal') {

    filteredTasks = allTasksCache.filter(task => {

      const created = String(task.createdByUserId || '');
      const assigned = String(task.assignedUserId || '');

      const noProject =
        task.projectId === null ||
        task.projectId === undefined ||
        task.projectId === 0 ||
        task.projectId === '0';

      // Your rule + safety net
      return (
        created === assigned ||
        noProject
      );
    });

  }

  // ----------------------
  // ALL
  // ----------------------
  else if (filter === 'all') {

    filteredTasks = allTasksCache;

  }

  // ----------------------
  // PROJECT
  // ----------------------
  else {

    filteredTasks = allTasksCache.filter(task =>
      String(task.projectId) === String(filter)
    );

  }

  renderTasks(filteredTasks);
}



function addTask() {
  const description = document.getElementById('taskInput').value.trim();
  if (!description) return;

  const projectIdRaw =
    document.getElementById('taskProjectSelect').value;

  const projectId = projectIdRaw ? Number(projectIdRaw) : null;

  const assignedUserId = projectId
    ? Number(document.getElementById('taskAssignedUserSelect').value)
    : currentUserId;

  const token = localStorage.getItem('authToken');

  fetch('/tasks', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({
      description,
      projectId,
      assignedUserId
    })
  })
    .then(res => {
      if (!res.ok) {
        return res.json().then(d => {
          throw new Error(d.message);
        });
      }
      document.getElementById('taskInput').value = '';
      loadTasks();
    })
    .catch(err => alert(err.message));
}


function toggleTask(taskID, isCompleted) {
  const token = localStorage.getItem('authToken');
  if (!token) return;

  fetch('/tasks', {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({
      taskID,
      isCompleted: isCompleted ? 1 : 0
    })
  })
    .then(res => {
      if (!res.ok) {
        return res.json().then(d => {
          throw new Error(d.message || 'Failed to update task');
        });
      }
    })
    .catch(err => {
      console.error('Update task error:', err);
      alert(err.message);
    });
}



function deleteTask(taskID) {
  if (!confirm('Delete this task?')) return;

  const token = localStorage.getItem('authToken');
  if (!token) return;

  fetch('/tasks', {
    method: 'DELETE',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({ taskID })
  })
    .then(res => {
      if (!res.ok) {
        return res.json().then(d => {
          throw new Error(d.message || 'Failed to delete task');
        });
      }
      loadTasks(); // uncomment when ready
    })
    .catch(err => {
      console.error('Delete task error:', err);
      alert(err.message);
    });
}

async function loadProjectsForTasks() {
  const token = localStorage.getItem('authToken');
  if (!token) return;

  try {
    const res = await fetch('/projects', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    const data = await res.json();

    // Depending on backend shape
    const projects = data.projects || data;

    populateTaskProjectFilter(projects);

  } catch (err) {
    console.error('Failed to load projects for tasks:', err);
  }
}

function populateTaskProjectFilter(tasks) {

  const select = document.getElementById('taskProjectFilter');
  if (!select) return;

  const projectMap = new Map();

  tasks.forEach(task => {
    if (
      task.projectId &&
      String(task.createdByUserId) !== String(task.assignedUserId)
    ) {
      projectMap.set(task.projectId, task.projectName);
    }
  });

  select.innerHTML = '';

  select.appendChild(new Option('All Projects', 'all'));
  select.appendChild(new Option('Personal', 'personal'));

  projectMap.forEach((name, id) => {
    select.appendChild(new Option(name, id));
  });

  const saved = localStorage.getItem('taskProjectFilter');
  if (saved) select.value = saved;
}


async function onTaskProjectChange() {
  const projectId = document.getElementById('taskProjectSelect').value;
  const assigneeSelect = document.getElementById('taskAssignedUserSelect');

  // PERSONAL TASK
  if (!projectId) {
    assigneeSelect.innerHTML =
      `<option value="${currentUserId}" selected>Myself</option>`;
    assigneeSelect.disabled = true;
    return;
  }

  // PROJECT TASK → LOAD PROJECT MEMBERS
  assigneeSelect.disabled = true;
  assigneeSelect.innerHTML = '<option>Loading...</option>';

  const token = localStorage.getItem('authToken');

  const res = await fetch(`/projects/${projectId}/users`, {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });

  const data = await res.json();

  assigneeSelect.innerHTML = '';

  data.users.forEach(u => {
    const opt = document.createElement('option');
    opt.value = u.id;
    opt.textContent = u.name || u.email;
    if (u.id === currentUserId) opt.selected = true;
    assigneeSelect.appendChild(opt);
  });

  assigneeSelect.disabled = false;
}


//// Blink the browsser when message arrives
let blinkInterval = null;
let originalTitle = document.title;

function notifyTabBlink(message = 'New Message!') {
  if (document.hasFocus()) return; // Don't blink if tab is already active

  let toggle = false;

  // Store the original title only once
  if (!originalTitle) {
    originalTitle = document.title;
  }

  // Avoid multiple intervals
  if (blinkInterval) return;

  blinkInterval = setInterval(() => {
    document.title = toggle ? message : originalTitle;
    toggle = !toggle;
  }, 1000);

  // Stop blinking when user returns to tab
  window.addEventListener('focus', stopTabBlink, { once: true });
}

function stopTabBlink() {
  clearInterval(blinkInterval);
  blinkInterval = null;
  document.title = originalTitle;
}
/////change password

function changePassword() {
  const newPassword = document.getElementById('newPassword').value.trim();
  const statusDiv = document.getElementById('passwordStatus');

  // -----------------------------
  // 1️⃣ Validate password
  // -----------------------------
  const allowed = /^[a-zA-Z0-9!@#$*]{6,}$/;
  if (!allowed.test(newPassword)) {
    statusDiv.innerText = 'Invalid password. Use at least 6 chars: a-z A-Z 0-9 !@#$*';
    statusDiv.style.color = 'red';
    return;
  }

  // -----------------------------
  // 2️⃣ Get JWT token
  // -----------------------------
  const token = localStorage.getItem('authToken');
  if (!token) {
    statusDiv.innerText = 'You are not logged in.';
    statusDiv.style.color = 'red';
    return;
  }

  // -----------------------------
  // 3️⃣ Make API call
  // -----------------------------
  fetch('/changePassword', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}` // send JWT instead of userId
    },
    body: JSON.stringify({ newPassword }) // no userId needed
  })
    .then(res => res.json())
    .then(data => {
      if (data.success) {
        statusDiv.innerText = 'Password changed successfully';
        statusDiv.style.color = 'green';
        document.getElementById('newPassword').value = '';
      } else {
        statusDiv.innerText = data.message || 'Error changing password';
        statusDiv.style.color = 'red';
      }
    })
    .catch(err => {
      console.error(err);
      statusDiv.innerText = 'Network error';
      statusDiv.style.color = 'red';
    });
}

/// END CHANGE PASSWORD
document.getElementById("toolsIcon").addEventListener("click", function () {
  const popup = document.querySelector(".tab-container");

  // Show it first
  popup.style.display = "flex";

  // Use requestAnimationFrame to allow the browser to apply display before scaling
  requestAnimationFrame(() => {
    popup.classList.add("flex");
  });
});

////TABS MANAGEMENT
function openTab(evt, tabId) {
  const tabPages = document.querySelectorAll('.tab-page');
  const tabButtons = document.querySelectorAll('.tab-btn');

  // Hide all tab content
  tabPages.forEach(tab => tab.classList.remove('active'));

  // Remove active class from all buttons
  tabButtons.forEach(btn => btn.classList.remove('active'));

  // Show current tab content
  document.getElementById(tabId).classList.add('active');

  // Add active class to clicked button
  evt.currentTarget.classList.add('active');
}

///open settings popup

document.getElementById("toolsIcon").addEventListener("click", function () {
  const popup = document.querySelector(".tab-container");
  const icon = this;

  // Get icon's center coordinates for transform-origin
  const rect = icon.getBoundingClientRect();
  const originX = `${rect.left + rect.width / 2}px`;
  const originY = `${rect.top + rect.height / 2}px`;

  // Apply transform origin as CSS variables
  popup.style.setProperty('--origin-x', originX);
  popup.style.setProperty('--origin-y', originY);

  // Show popup and trigger animation
  popup.classList.remove("hidden");

  // Ensure transition starts after the DOM applies new styles
  requestAnimationFrame(() => {
    popup.classList.add("show");
  });
});

// closing the settings popup dialog
function closePopup() {
  const popup = document.querySelector(".tab-container");
  const icon = document.getElementById("toolsIcon");

  // Get icon's center for transform-origin
  const rect = icon.getBoundingClientRect();
  const originX = `${rect.left + rect.width / 2}px`;
  const originY = `${rect.top + rect.height / 2}px`;
  popup.style.setProperty('--origin-x', originX);
  popup.style.setProperty('--origin-y', originY);

  // Trigger reverse animation
  popup.classList.remove("show");

  // After transition ends, hide the element
  setTimeout(() => {
    popup.style.display = "none";
    popup.classList.remove("flex"); // optional cleanup
  }, 300); // Match the CSS transition duration
}


////password tooggle

document.getElementById("togglePassword").addEventListener("click", function () {
  const passwordInput = document.getElementById("newPassword");
  const icon = this;

  const isVisible = passwordInput.type === "text";
  passwordInput.type = isVisible ? "password" : "text";
  icon.name = isVisible ? "eye-outline" : "eye-off-outline";
});


//////Update Avatar

let cropper;
const input = document.getElementById('imageInput');
const preview = document.getElementById('preview');

input.addEventListener('change', (e) => {
  const file = e.target.files[0];
  if (!file) return;

  const reader = new FileReader();
  reader.onload = () => {
    preview.src = reader.result;

    preview.onload = () => {
      if (cropper) cropper.destroy();

      cropper = new Cropper(preview, {
        aspectRatio: 1,
        viewMode: 1,
        dragMode: 'move',
        autoCropArea: 0.1,              // Small crop box (approx 10% of image)
        cropBoxMovable: true,
        cropBoxResizable: true,
        background: true,
        responsive: true,
        zoomOnWheel: true,
        zoomOnTouch: false,
        minCropBoxWidth: 50,
        minCropBoxHeight: 50,
        ready() {
          // Manually set crop box size (100x100)
          cropper.setCropBoxData({
            width: 100,
            height: 100
          });
        }
      });
    };
  };
  reader.readAsDataURL(file);
});


document.getElementById('uploadBtn').addEventListener('click', async () => {
  if (!cropper) return;
  const uploadMsg = document.getElementById('uploadAlert');

  const token = localStorage.getItem('authToken');
  if (!token) {
    uploadMsg.innerText = 'You are not logged in.';
    uploadMsg.style.color = 'red';
    return;
  }

  const canvas = cropper.getCroppedCanvas({
    width: 100,
    height: 100,
    imageSmoothingQuality: 'high'
  });

  canvas.toBlob(async (blob) => {
    const formData = new FormData();
    formData.append('file', blob, 'avatar.png');
    // remove senderId, server identifies user via JWT

    try {
      const res = await fetch('/api/avatars', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}` // JWT header
        },
        body: formData
      });

      const data = await res.json();

      if (res.ok) {
        uploadMsg.innerText = 'Upload successful!';
        uploadMsg.style.color = 'green';

        currentAvatar = data.path; // update current avatar
        renderWelcomeBox(data.userId || currentUserId); // update avatar in UI

        // notify others via socket
        socket.emit('avatar_updated', {
          userId: data.userId || currentUserId,
          avatar: currentAvatar
        });

      } else {
        uploadMsg.innerText = data.message || 'Upload failed';
        uploadMsg.style.color = 'red';
      }

    } catch (err) {
      console.error(err);
      uploadMsg.innerText = 'Network error';
      uploadMsg.style.color = 'red';
    }

  }, 'image/png');
});


//If message interpreted as code then add copy icon
function copyCodeToClipboard(codeId) {
  const codeElement = document.getElementById(codeId);
  if (!codeElement) return;

  const text = codeElement.innerText;
  navigator.clipboard.writeText(text).then(() => {
    const btn = event.target;
    btn.innerHTML = '<i class="fa-solid fa-check"></i> Copied';
    setTimeout(() => btn.innerHTML = '<i class="fa-solid fa-copy"></i> Copy', 2000);
  }).catch(err => {
    console.error('Copy failed', err);
  });
}

////EMOJI FUNCTIONS

function toggleSettings() {
  // Check the current display style and toggle
  if (frmSettings.style.display === 'none' || frmSettings.style.display === '') {
    frmSettings.style.display = 'flex'; // Show the frmSettings
  } else {
    frmSettings.style.display = 'none'; // Hide the frmSettings
  }
}

////FIREBASE

// Your Firebase config here (replace with your actual values)
const firebaseConfig = {
  apiKey: "AIzaSyADlnvjo9raZnFqo_kuxh6JJ0XwUL4qPco",
  authDomain: "gocrm-3ab18.firebaseapp.com",
  projectId: "gocrm-3ab18",
  storageBucket: "gocrm-3ab18.firebasestorage.app",
  messagingSenderId: "653626734920",
  appId: "1:653626734920:web:a639f3543f238b6fbe1b7d"
};

// Initialize Firebase
//import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.22.2/firebase-app.js';
//import { getMessaging } from 'https://www.gstatic.com/firebasejs/9.22.2/firebase-messaging.js';

const app = firebase.initializeApp(firebaseConfig);


// Get messaging object
const messaging = firebase.messaging();

function registerFCMToken() {

  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/firebase-messaging-sw.js')
      .then(async registration => {
        // console.log('? Service Worker registered:', registration);

        // Wait until the service worker is active
        await navigator.serviceWorker.ready;

        // Ask notification permission
        const permission = await Notification.requestPermission();
        if (permission === 'granted') {
          //console.log('? Notification permission granted');

          messaging.getToken({
            vapidKey: 'BBs4aYg3aWT6nIE6dPIVzVnKROdJIhPNipUUdZ_2NeOD5irLkA--GOidezZ6SIU0XkvORj38gK1l8QW3NXocbeg',
            serviceWorkerRegistration: registration // ? use this instead of useServiceWorker()
          })
            .then(token => {
              if (token) {
                //console.log('? FCM Token:', token);
                sendTokenToServer(token);
              } else {
                console.warn('?? No FCM token retrieved.');
              }
            })
            .catch(err => {
              console.error('? Error getting FCM token:', err);
            });
        } else {
          console.warn('?? Notification permission denied');
        }
      })
      .catch(err => {
        console.error('? Service Worker registration failed:', err);
      });
  }
}

function sendTokenToServer(fcmToken) {
  if (!fcmToken || typeof fcmToken !== 'string') return;

  const token = localStorage.getItem('authToken'); // JWT
  if (!token) {
    console.warn('Cannot send FCM token: user not logged in.');
    return;
  }

  fetch('/users/save-fcm-token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}` // JWT header
    },
    body: JSON.stringify({ token: fcmToken }) // no userId needed
  })
    .then(res => res.json())
    .then(data => {
      if (!data.success) console.warn('Failed to save FCM token:', data.error || data.message);
    })
    .catch(err => {
      console.error('Error sending FCM token to server:', err);
    });
}


// function to adjust dateTime to local date time using offset
// obtained from users' PC/device
function convertDateTime(dateToConvert) {
  // Convert input to Date object (supports string or Date)
  const utcDate = new Date(dateToConvert);

  // Add offset in milliseconds
  const localDate = new Date(utcDate.getTime() + offsetMinutes * 60000);

  // Format as "YYYY-MM-DD HH:mm:ss"
  return localDate.toISOString().slice(0, 19).replace('T', ' ');
}

//socket reconnection utils etc

const updateStatus = (message) => {
  const statusDiv = document.getElementById("connection-status");
  if (statusDiv) {
    statusDiv.innerText = message;
  } else {
    console.log(message); // Fallback to console if no status element exists
  }
  if (message == "Connected") {
    setConnected();
  }
  else {
    setDisconnected();
  }
};

// save local user preferencees

//update a setting
function updateSetting(key, value) {
  const settings = JSON.parse(localStorage.getItem('chatSettings') || '{}');
  settings[key] = value;
  localStorage.setItem('chatSettings', JSON.stringify(settings));
}
// get a settings value
function getSetting(key) {
  const settings = JSON.parse(localStorage.getItem('chatSettings') || '{}');
  return settings[key];
}
//get all settings
function getAllSettings() {
  return JSON.parse(localStorage.getItem('chatSettings') || '{}');
}

function loadSettings() {
  const settings = getAllSettings();

  // enterIsSend
  isEnterSendEnabled = settings.enterIsSend ?? 1;
  document.getElementById('enterIsSendCheckbox').checked = isEnterSendEnabled === 1;

  // playNotificationSound
  playNotificationSound = settings.playNotificationSound ?? 1;
  document.getElementById('playNotificationSound').checked = playNotificationSound === 1;

  // showNotifications
  showNotifications = settings.showNotifications ?? 1;
  document.getElementById('showNotifications').checked = showNotifications === 1;
}


/////////////////////////////////////////////////
// project management file and docs management//
////////////////////////////////////////////////

// right wrapper tabs management
document.querySelectorAll('.proj-tab-btn').forEach(button => {
  button.addEventListener('click', () => {
    // Remove active class from all buttons and panels
    document.querySelectorAll('.proj-tab-btn').forEach(btn => btn.classList.remove('active'));
    document.querySelectorAll('.proj-tab-panel').forEach(panel => panel.classList.remove('active'));

    // Add active class to clicked button and associated panel
    button.classList.add('active');
    document.getElementById(button.dataset.tab).classList.add('active');
  });
});

//chat.js

async function previewFile(projectId, fileId, filename) {

  const token = localStorage.getItem('authToken');
  if (!token) {
    alert('You are not logged in.');
    return;
  }

  const ext = filename.split('.').pop().toLowerCase();

  const previewable = [
    'jpg', 'jpeg', 'png', 'gif',
    'txt', 'pdf', 'docx', 'xlsx'
  ];

  const container = document.getElementById('filePreview');
  container.innerHTML = '';
  container.style.display = 'block';
  container.classList.add('popup-preview');

  // Close button
  const closeBtn = document.createElement('button');
  closeBtn.textContent = '×';
  closeBtn.className = 'closeBtn';
  closeBtn.onclick = closePreview;
  container.appendChild(closeBtn);

  if (!previewable.includes(ext)) {
    container.innerHTML += `<p>Preview not available.</p>`;
    return;
  }

  // SAAS secure preview route
  const fileUrl =
    `/projects/${projectId}/files/${fileId}/preview`;

  try {

    const res = await fetch(fileUrl, {
      headers: {
        Authorization: `Bearer ${token}`
      }
    });

    if (!res.ok) {
      container.innerHTML += `<p>Access denied or file not found.</p>`;
      return;
    }

    const blob = await res.blob();
    const objectUrl = URL.createObjectURL(blob);

    // -----------------------------
    // Render preview
    // -----------------------------

    if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) {

      const img = document.createElement('img');
      img.src = objectUrl;
      img.style.maxWidth = '100%';
      container.appendChild(img);

    }
    else if (ext === 'txt') {

      const text = await blob.text();
      const pre = document.createElement('pre');
      pre.textContent = text;
      pre.style.whiteSpace = 'pre-wrap';
      container.appendChild(pre);

    }
    else if (ext === 'pdf') {

      const embed = document.createElement('embed');
      embed.src = objectUrl;
      embed.type = 'application/pdf';
      embed.width = '100%';
      embed.height = '500px';
      container.appendChild(embed);

    }
    else {

      // Office files fallback → download
      const downloadLink = document.createElement('a');
      downloadLink.href = objectUrl;
      downloadLink.download = filename;
      downloadLink.textContent = 'Download file';
      container.appendChild(downloadLink);

    }

  } catch (err) {
    console.error('Preview error:', err);
    container.innerHTML += `<p>Failed to preview file.</p>`;
  }
}


//Load Project Files Tree View
async function loadProjectTree() {

  const token = localStorage.getItem('authToken');
  if (!token) return;

  try {

    const res = await fetch(`/projects/files/tree`, {
      headers: {
        Authorization: `Bearer ${token}`
      }
    });

    const data = await res.json();
    if (!data.success) return;

    const treeContainer = document.getElementById('projectTree');
    treeContainer.innerHTML = '';

    // ============================
    // LEGACY STRUCTURE PRESERVED
    // ============================

    for (const [projectName, content] of Object.entries(data.tree)) {

      const projectId = content.slug;

      const projectNode = document.createElement('div');
      projectNode.className = 'project-node';

      const projectHeader = document.createElement('div');
      projectHeader.className = 'project-title';
      projectHeader.innerHTML =
        `<ion-icon name="cube-outline"></ion-icon> ${projectName}`;

      projectNode.appendChild(projectHeader);


      ['docs', 'images'].forEach(type => {

        const folderDetails = document.createElement('details');
        folderDetails.classList.add('folder-toggle');

        const summary = document.createElement('summary');

        const folderTitleDiv = document.createElement('div');
        folderTitleDiv.classList.add('folder-title');

        if (type === 'docs') {
          folderTitleDiv.classList.add('file-node');
          folderTitleDiv.innerHTML =
            `<ion-icon name="document-outline"></ion-icon> Docs`;
        } else {
          folderTitleDiv.classList.add('image-node');
          folderTitleDiv.innerHTML =
            `<ion-icon name="image-outline"></ion-icon> Images`;
        }

        summary.appendChild(folderTitleDiv);
        folderDetails.appendChild(summary);


        // ============================
        // FILE LIST
        // ============================

        const fileList = document.createElement('ul');
        fileList.className = 'file-list';

        if (content[type]?.length) {

          content[type].forEach(fileObj => {

            const fileId = fileObj.id;
            const file = fileObj.filename;
            const uploaderId = fileObj.uploaderId;

            const li = document.createElement('li');

            li.classList.add(
              type === 'docs' ? 'file-object' : 'image-object'
            );

            const icon = getFileIcon(file);

            // ============================
            // FILE PREVIEW LINK
            // ============================

            const fileLink = document.createElement('span');
            fileLink.className = 'file-link';
            fileLink.innerHTML = `${icon} ${file}`;
            fileLink.style.cursor = 'pointer';

            fileLink.onclick = () => {
              previewFile(projectId, fileId, file);
            };

            li.appendChild(fileLink);


            // ============================
            // DELETE ICON (ADMIN OR UPLOADER)
            // ============================

            const isUploader = Number(uploaderId) === Number(currentUserId);
            const isAdminUser = window.isAdmin === 1;

            if (isUploader || isAdminUser) {

              const delBtn = document.createElement('ion-icon');
              delBtn.setAttribute('name', 'trash-outline');
              delBtn.className = 'file-delete-icon';
              delBtn.title = 'Delete file';

              delBtn.onclick = (e) => {
                e.stopPropagation();
                deleteProjectFile(projectId, fileId);
              };

              li.appendChild(delBtn);
            }


            fileList.appendChild(li);
          });
        }


        // ============================
        // UPLOAD FORM
        // ============================

        const uploadForm = document.createElement('form');
        uploadForm.className = 'treeForm';

        uploadForm.onsubmit = (e) =>
          uploadProjectFile(e, projectId, type);

        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.name = 'file';
        fileInput.required = true;

        const uploadBtn = document.createElement('button');
        uploadBtn.type = 'submit';
        uploadBtn.className = 'uploadTreeBtn'
        uploadBtn.textContent = 'Upload';

        uploadForm.appendChild(fileInput);
        uploadForm.appendChild(uploadBtn);

        folderDetails.appendChild(fileList);
        folderDetails.appendChild(uploadForm);

        projectNode.appendChild(folderDetails);
      });

      treeContainer.appendChild(projectNode);
    }

  } catch (err) {
    console.error('Project tree load error:', err);
  }
}

/// delete project files

async function deleteProjectFile(projectSlug, folderType, filename) {
  if (!confirm('Are you sure you want to delete this file?')) return;

  const token = localStorage.getItem('authToken');
  if (!token) {
    alert('You are not logged in.');
    return;
  }

  try {
    const res = await fetch('/proj_delete_file', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}` // ✅ JWT
      },
      body: JSON.stringify({
        projectSlug,
        folderType,
        filename
      }) // ✅ no userId / isAdmin
    });

    const result = await res.json();

    if (res.ok && result.success) {
      alert('File deleted');
      loadProjectTree(); // ✅ no userId needed
    } else {
      alert('Deletion failed: ' + (result.message || result.error || 'Unknown error'));
    }

  } catch (err) {
    console.error('Error deleting project file:', err);
    alert('Network error while deleting file');
  }
}


// toggle icons
document.querySelectorAll('details > summary').forEach(summary => {
  const details = summary.parentNode;
  summary.addEventListener('click', () => {
    const icon = summary.querySelector('.toggle-icon');
    setTimeout(() => {
      icon.className = `fa-regular ${details.open ? 'fa-square-minus' : 'fa-square-plus'} toggle-icon`;
    }, 0);
  });
});

function getFileIcon(filename) {
  const ext = filename.split('.').pop().toLowerCase();
  const map = {
    pdf: 'fa-file-pdf',
    txt: 'fa-file-lines',
    doc: 'fa-file-word',
    docx: 'fa-file-word',
    xls: 'fa-file-excel',
    xlsx: 'fa-file-excel',
    png: 'fa-file-image',
    jpg: 'fa-file-image',
    jpeg: 'fa-file-image',
    bmp: 'fa-file-image',
    gif: 'fa-file-image',
    wav: 'fa-file-audio',
    mp3: 'fa-file-audio',
    mp4: 'fa-file-video',
    avi: 'fa-file-video',
    zip: 'fa-file-zipper',
    rar: 'fa-file-zipper'
  };
  const iconClass = map[ext] || 'fa-file';
  return `<i class="fa-regular ${iconClass}"></i>`;
}

function closePreview() {
  const container = document.getElementById('filePreview');
  container.style.display = 'none';
  container.innerHTML = '';
}




async function uploadProjectFile(event, projectSlug, folderType) {
  event.preventDefault();

  const form = event.target;
  const fileInput = form.querySelector('input[type="file"]');
  if (!fileInput.files.length) return;

  const formData = new FormData();
  formData.append('file', fileInput.files[0]);
  formData.append('folderType', folderType);

  const res = await fetch(`/projects/${projectSlug}/files/upload`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${localStorage.getItem('authToken')}`,
    },
    body: formData,
  });

  const result = await res.json();

  if (result.success) {
    alert('File uploaded');

    // refresh full tree (org scoped)
    loadProjectTree();

  } else {
    alert('Upload failed: ' + (result.error || result.message || 'Unknown error'));
  }
}




document.querySelectorAll('.proj-tab-btn').forEach(button => {
  button.addEventListener('click', () => {
    // Remove active class from all buttons and panels
    document.querySelectorAll('.proj-tab-btn').forEach(btn => btn.classList.remove('active'));
    document.querySelectorAll('.proj-tab-panel').forEach(panel => panel.classList.remove('active'));

    // Add active class to clicked button and associated panel
    button.classList.add('active');
    document.getElementById(button.dataset.tab).classList.add('active');
  });
});

//device type check

function isMobileDevice() {
  const userAgentCheck = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  const screenSizeCheck = window.matchMedia("(max-width: 768px)").matches;
  return userAgentCheck || screenSizeCheck;
}

if (isMobileDevice()) {
  isMobile = true;
} else {
  isMobile = false;
}

// to parse messages and detect URL's and add < ahref tags to make them clickable
function parseLinks(message) {
  const urlRegex = /(https?:\/\/[^\s]+)/g;
  return message.replace(urlRegex, (url) => {
    return `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
  });
}

//escape script tags
function escapeHTML(str) {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

function escapeAttr(str) {
  if (typeof str !== 'string') return '';
  return str
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}


// reply to functionality and helpers
function getSnippet(str) {
  if (!str) return '';
  return str.length > 20 ? str.slice(0, 60) + '…' : str;
}

//Functions to Set and Clear Reply Context
function setReplyContext(message) {
  replyContext = {
    id: message.id,
    senderName: message.senderName,
    message: message.message,
    filename: message.filename,
    isFile: message.isFile
  };
  renderReplyPreview(replyContext);
}

function clearReplyPreview() {
  replyContext = null;
  const preview = document.getElementById('replyPreview');
  if (preview) preview.remove();
}

///rendering reply function
function renderReplyPreview(context) {
  let replyBox = document.getElementById('replyPreview');
  if (!replyBox) {
    replyBox = document.createElement('div');
    replyBox.id = 'replyPreview';
    replyBox.className = 'reply-preview';
    document.getElementById('chatInputWrapper').prepend(replyBox);
  }

  replyBox.innerHTML = `
    <span class="replyHeader">Replying to ${context.senderName}:</span><br>
    <span class="original-message">${getSnippet(context.message || context.filename)}</span>
    <button onclick="clearReplyPreview()" style="margin-left:10px;">✖</button>
  `;
}

function formatTimeAMPM(timestamp) {
  if (!timestamp) return '';
  try {
    const date = new Date(timestamp);
    return date.toLocaleTimeString([], {
      hour: '2-digit',
      minute: '2-digit',
      hour12: true,
    });
  } catch (err) {
    console.error('Invalid timestamp passed to formatTimeAMPM:', timestamp);
    return '';
  }
}

//prevent back button of android from taking user to history.back

// Push a dummy state to the history stack
history.pushState(null, null, location.href);

// Listen for the back button (popstate event)
window.addEventListener('popstate', function (event) {
  // Re-push the same state to prevent going back
  history.pushState(null, null, location.href);
});


//////////message forwarding

let forwardPayload = null;

function forwardMessage(msg) {
  forwardPayload = {
    message: msg.message || '',
    file: msg.file || msg.filepath || '',
    isFile: !!msg.isFile,
    originalSender: msg.senderName || 'Unknown'
  };

  openForwardUserSelector();
}

function openForwardUserSelector() {
  const modal = document.getElementById('forwardModal');
  const list = document.getElementById('forwardUserList');
  list.innerHTML = '';

  allUsers.forEach(user => {
    if (user.id !== currentUserId) {
      const wrapper = document.createElement('div');

      const checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
      checkbox.value = user.id;
      checkbox.id = `fwd-${user.id}`;

      const label = document.createElement('label');
      label.htmlFor = checkbox.id;
      label.textContent = ` ${user.username}`;

      wrapper.appendChild(checkbox);
      wrapper.appendChild(label);
      list.appendChild(wrapper);
    }
  });

  modal.classList.remove('hidden');
}

function closeForwardModal() {
  document.getElementById('forwardModal').classList.add('hidden');
  forwardPayload = null;
}

function confirmForward() {
  const selectedIds = Array.from(document.querySelectorAll('#forwardUserList input[type="checkbox"]:checked'))
    .map(cb => Number(cb.value));

  selectedIds.forEach(receiverId => {
    const payload = {
      senderId: Number(currentUserId),
      receiverId,
      isRead: 0,
      forwardedFrom: forwardPayload.originalSender
    };

    if (forwardPayload.isFile) {
      payload.isFile = true;
      payload.file = forwardPayload.file;
    } else {
      payload.message = forwardPayload.message;
    }

    socket.emit('send_message', payload);
  });

  closeForwardModal();
}

/// CONNECTION STATUS BEACON

const connStatusEl = document.getElementById('connStatus');

function setConnected() {
  connStatusEl.classList.remove('disconnected');
  connStatusEl.classList.add('connected');
}

function setDisconnected() {
  connStatusEl.classList.remove('connected');
  connStatusEl.classList.add('disconnected');
}


////////////////////////////////////////////////
//Search for text in chat partial or exact words
////////////////////////////////////////////////
let matchMode = 'partial'; // default mode
let matches = [], currentIndex = 0;

const modeRadios = document.querySelectorAll('input[name="matchMode"]');
const toggleSearchBtn = document.getElementById('toggleSearch');
const searchWrapper = document.getElementById('searchWrapper');
const searchInput = document.getElementById('chatSearch');
const searchCounter = document.getElementById('searchCounter');
const btnPrev = document.getElementById('searchPrev');
const btnNext = document.getElementById('searchNext');

// 🔁 Radio mode toggle
modeRadios.forEach(radio => {
  radio.addEventListener('change', () => {
    matchMode = radio.value;
    performSearch(searchInput.value.trim()); // re-run search
  });
});

// 🔍 Input change triggers search
searchInput.addEventListener('input', () => {
  performSearch(searchInput.value.trim());
});

// 🔄 Previous/Next navigation
btnPrev.addEventListener('click', () => {
  if (!matches.length) return;
  currentIndex = (currentIndex - 1 + matches.length) % matches.length;
  updateCounter(currentIndex + 1, matches.length);
  scrollToMatch(currentIndex);
});

btnNext.addEventListener('click', () => {
  if (!matches.length) return;
  currentIndex = (currentIndex + 1) % matches.length;
  updateCounter(currentIndex + 1, matches.length);
  scrollToMatch(currentIndex);
});

// 🔄 Main search logic
function performSearch(keyword) {
  clearHighlights();

  if (!keyword) {
    updateCounter(0, 0);
    return;
  }

  const bubbles = document.querySelectorAll('.bubble');
  matches = [];

  const regex = matchMode === 'exact'
    ? new RegExp(`\\b(${escapeRegExp(keyword)})\\b`, 'gi')
    : new RegExp(`(${escapeRegExp(keyword)})`, 'gi');

  bubbles.forEach(bubble => {
    const contentDiv = bubble.querySelector('.message-content');
    if (!contentDiv) return;

    if (!contentDiv.dataset.original) {
      contentDiv.dataset.original = contentDiv.innerHTML;
    }

    // Skip if already highlighted before
    if (contentDiv.dataset.original.includes('highlighted-search')) {
      return;
    }

    const matchFound = matchMode === 'exact'
      ? contentDiv.innerText.match(regex)
      : contentDiv.innerText.toLowerCase().includes(keyword.toLowerCase());

    if (matchFound) {
      matches.push(bubble);
      contentDiv.innerHTML = contentDiv.innerHTML.replace(regex, `<span class="highlighted-search">$1</span>`);
    } else {
      // Restore if no match
      if (contentDiv.dataset.original) {
        contentDiv.innerHTML = contentDiv.dataset.original;
        delete contentDiv.dataset.original;
      }
    }
  });

  updateCounter(1, matches.length);
  currentIndex = 0;

  if (matches.length > 0) {
    scrollToMatch(currentIndex);
    enableNav(true);
  } else {
    enableNav(false);
  }
}



// 🧹 Clear highlights from all bubbles
function clearHighlights() {
  matches.forEach(bubble => {
    const contentDiv = bubble.querySelector('.message-content');
    if (contentDiv && contentDiv.dataset.original) {
      contentDiv.innerHTML = contentDiv.dataset.original;
      delete contentDiv.dataset.original;
    }
    bubble.classList.remove('current-match');
  });

  matches = [];
  currentIndex = 0;
}



// 🔢 Update counter
function updateCounter(current, total) {
  searchCounter.textContent = `${total ? current : 0} / ${total}`;
}

// 🚀 Scroll to match
function scrollToMatch(index) {
  matches.forEach(m => m.classList.remove('current-match'));
  const match = matches[index];
  match.classList.add('current-match');
  match.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

// ⬆️⬇️ Enable nav buttons
function enableNav(enabled) {
  btnPrev.disabled = !enabled;
  btnNext.disabled = !enabled;
}

// 🔁 Toggle search panel
function toggleSearchPanel(force = null) {
  const isOpen = searchWrapper.classList.contains('expanded');

  if (force === false || (isOpen && force !== true)) {
    searchWrapper.classList.remove('expanded');
    clearSearchUI();
    return;
  }

  if (!isOpen || force === true) {
    searchWrapper.classList.add('expanded');
    setTimeout(() => searchInput.focus(), 200);
  }
}


toggleSearchBtn.addEventListener('click', toggleSearchPanel);

// 🛑 ESC to close panel
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && searchWrapper.classList.contains('expanded')) {
    toggleSearchPanel(false); // collapse cleanly
    return;
  }

  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'f') {
    e.preventDefault();
    toggleSearchPanel(true); // force open
  }

  // arrow key navigation...
});

// 🔄 Clear input + matches
function clearSearchUI() {
  searchInput.value = '';
  clearHighlights();
  updateCounter(0, 0);
  enableNav(false);
}

// 🔐 Escape regex special chars
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

///search with up down arrow on keyboard
document.addEventListener('keydown', (e) => {
  // ESC to close search panel
  if (e.key === 'Escape' && searchWrapper.classList.contains('expanded')) {
    toggleSearchPanel();
    return;
  }

  // Don't scroll if input isn't visible (not searching)
  if (!searchWrapper.classList.contains('expanded') || !matches.length) return;

  if (e.key === 'ArrowDown') {
    currentIndex = (currentIndex + 1) % matches.length;
    updateCounter(currentIndex + 1, matches.length);
    scrollToMatch(currentIndex);
  }

  if (e.key === 'ArrowUp') {
    currentIndex = (currentIndex - 1 + matches.length) % matches.length;
    updateCounter(currentIndex + 1, matches.length);
    scrollToMatch(currentIndex);
  }
});


/*----------------------------
//NEW ADDITIONS FOR GROUP CHAT
------------------------------*/

document.querySelectorAll('.left-tab').forEach(tab => {
  tab.addEventListener('click', () => {
    // Update tab UI
    document.querySelectorAll('.left-tab').forEach(t => t.classList.remove('active'));
    tab.classList.add('active');

    activeLeftTab = tab.dataset.tab;

    const userList = document.getElementById('userList');
    const roomsTree = document.getElementById('projectRoomsTree');

    if (!userList || !roomsTree) {
      console.warn('Required left pane elements missing');
      return;
    }

    // -----------------------------
    // CHAT TAB
    // -----------------------------
    if (activeLeftTab === 'chat') {
      // 🔑 IMPORTANT: leave any project room
      leaveCurrentProjectRoom();

      // Reset context to direct chat
      chatContext = { mode: 'direct', projectId: null, projectName: null };

      //  reload only DIRECT chat history if a user is selected
      reloadCurrentChatHistory();

      userList.style.display = 'flex';
      roomsTree.classList.add('hidden');
      return;
    }

    // -----------------------------
    // ROOMS TAB
    // -----------------------------
    userList.style.display = 'none';
    roomsTree.classList.remove('hidden');

    loadProjectRooms(); // lazy load

  });
});


async function loadProjectRooms() {
  const token = localStorage.getItem('authToken');
  if (!token) return;

  try {
    const res = await fetch('/projects', {
      headers: { Authorization: `Bearer ${token}` }
    });

    const data = await res.json();
    const projects = data.projects || data;

    renderProjectRoomsTree(projects);
  } catch (err) {
    console.error('Failed to load project rooms:', err);
  }
}

async function loadProjectRoomHistory(reset = false) {
  if (!chatContext.projectId) return;

  if (reset) {
    chatMessages.innerHTML = '';
    chatCursor = null;
  }

  const url = new URL('/api/project-room/history', window.location.origin);
  url.searchParams.set('projectId', chatContext.projectId);
  url.searchParams.set('roomId', chatContext.roomId || 0);
  if (chatCursor) url.searchParams.set('before', chatCursor);

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${authToken}` }
  });

  const data = await res.json();
  if (!data.items) return;

  data.items.forEach(msg => appendMessage(msg));
  chatCursor = data.nextCursor;
}

async function renderProjectRoomsTree(projects) {
  const container = document.getElementById('projectRoomsTree');
  container.innerHTML = '';

  for (const project of projects) {

    // PROJECT HEADER
    const projectHeader = document.createElement('div');
    projectHeader.className = 'project-node';

    const caret = document.createElement('span');
    caret.className = 'project-caret';
    caret.textContent = '▶';

    const title = document.createElement('span');
    title.textContent = project.name;
    title.style.cursor = 'pointer';

    projectHeader.appendChild(caret);
    projectHeader.appendChild(title);
    container.appendChild(projectHeader);

    // USERS CONTAINER
    const usersContainer = document.createElement('div');
    usersContainer.className = 'project-users';
    container.appendChild(usersContainer);

    let loaded = false;

    // TOGGLE USERS
    caret.addEventListener('click', async (e) => {
      e.stopPropagation();

      const expanded = usersContainer.classList.toggle('expanded');
      caret.textContent = expanded ? '▼' : '▶';

      if (expanded && !loaded) {
        loaded = true;

        try {
          const res = await fetch(`/projects/${project.id}/users`, {
            headers: {
              Authorization: `Bearer ${localStorage.getItem('authToken')}`
            }
          });

          const data = await res.json();
          const users = Array.isArray(data)
            ? data
            : Array.isArray(data.users)
              ? data.users
              : [];

          if (!users.length) {
            console.warn('No users found for project', project.id, data);
          }

          let renderedCount = 0;

          users.forEach(user => {
            if (user.id === currentUserId) return;

            renderedCount++;

            const userDiv = document.createElement('div');
            userDiv.className = 'user-item project-user';
            userDiv.dataset.userid = user.id;

            const displayName =
              user.name ||
              `${user.firstName || ''} ${user.lastName || ''}`.trim() ||
              user.email ||
              'User';

            const fullUser =
              allUsers.find(u => u.id === user.id) || user;

            userDiv.innerHTML = `
              ${buildUserAvatar(fullUser)}
              <span class="username">${displayName}</span>
            `;

            userDiv.onclick = () => {
              openProjectUserChat(project, user);
            };

            usersContainer.appendChild(userDiv);
          });

          // 👇 ADD THIS
          if (renderedCount === 0) {
            const emptyDiv = document.createElement('div');
            emptyDiv.style.fontSize = '12px';
            emptyDiv.style.color = '#666';
            emptyDiv.style.padding = '6px 6px 6px 24px';
            emptyDiv.textContent = '— No other members —';
            usersContainer.appendChild(emptyDiv);

            // initialize unread count
            if (!projectUnreadCount[project.id]) {
              projectUnreadCount[project.id] = 0;
            }

            const badge = getOrCreateProjectBadge(project.id, projectHeader);

            if (projectUnreadCount[project.id] > 0) {
              badge.textContent = projectUnreadCount[project.id];
              badge.style.display = 'inline-block';
            } else {
              badge.style.display = 'none';
            }

          }


        } catch (err) {
          console.warn('Failed loading project users', err);
        }
      }
    });

    // CLICK PROJECT NAME → OPEN PROJECT ROOM
    title.addEventListener('click', () => {
      openProjectRoom(project);
    });
  }
}


function openProjectRoom(project) {
  //console.log('Opening project room:', project);

  chatContext = {
    mode: 'project-room',
    projectId: project.id,
    projectName: project.name,
    roomId: 0
  };

  selectedUserId = null;
  chatMessages.innerHTML = '';
  typingStatus.textContent = '';

  updateChatHeader(`Project: ${project.name}`);

  // ✅ Join socket room ONLY for live messages
  joinProjectRoom(project.id);

  // ✅ Load project room chat history via HTTPS
  reloadCurrentChatHistory(true);

  document.getElementById('buttonsContainer').style.display = 'flex';

  // Clear unread badge for this project
  if (typeof projectUnreadCount !== 'undefined') {
    projectUnreadCount[project.id] = 0;
  }
}



function openProjectUserChat(project, user) {
  chatContext = {
    mode: 'project-user',
    projectId: project.id,
    projectName: project.name
  };

  selectedUserId = user.id;
  chatMessages.innerHTML = '';
  typingStatus.textContent = '';

  updateChatHeader(`${user.name || user.email} @ ${project.name}`);

  // 🔑 Ensure we are in the correct project room
  joinProjectRoom(project.id);

  // load chat history
  document.getElementById('buttonsContainer').style.display = 'flex';

  // clear unread for this project (if you added STEP 2)
  if (typeof projectUnreadCount !== 'undefined') {
    projectUnreadCount[project.id] = 0;
  }
}


function getOrCreateProjectBadge(projectId, projectHeaderEl) {
  let badge = projectHeaderEl.querySelector('.project-unread-badge');

  if (!badge) {
    badge = document.createElement('span');
    badge.className = 'project-unread-badge';
    badge.textContent = '0';
    projectHeaderEl.appendChild(badge);
  }

  return badge;
}

function buildUserAvatar(user) {
  const pastelColors = [
    '#FF70A6',
    '#70DEFF',
    '#FFD670',
    '#FF9770',
    '#C0B0EC',
    '#7EC4CF'
  ];

  let avatarHTML = '';

  if (user.avatar) {
    const avatarUrl = user.avatar.startsWith('http')
      ? user.avatar
      : `/${user.avatar}`;

    avatarHTML = `<img src="${avatarUrl}" class="avatar pic">`;
  } else {
    const initials = getInitials(
      user.name ||
      `${user.firstName || ''} ${user.lastName || ''}`.trim() ||
      user.email ||
      'U'
    );

    const color = pastelColors[user.id % pastelColors.length];

    avatarHTML = `
      <div class="avatar initials" style="background:${color}">
        ${initials}
      </div>`;
  }

  const statusDot = `
    <span class="status-indicator ${user.is_online ? 'online' : 'offline'}"></span>
  `;

  return avatarHTML + statusDot;
}
function leaveCurrentProjectRoom() {
  if (currentProjectRoomId && socket?.connected) {
    socket.emit('leave_project_room', { projectId: currentProjectRoomId });
    currentProjectRoomId = null;
  }
}

function joinProjectRoom(projectId) {
  if (!socket?.connected) return;

  if (currentProjectRoomId && currentProjectRoomId !== projectId) {
    leaveCurrentProjectRoom();
  }

  socket.emit('join_project_room', { projectId });
  currentProjectRoomId = projectId;
}

function reloadCurrentChatHistory(reset = true) {
  if (!chatContext || !chatContext.mode) return;

  if (chatContext.mode === 'direct') {
    if (!selectedUserId) return;
    loadUserChatHistory(reset);
    return;
  }

  if (chatContext.mode === 'project-room') {
    if (!chatContext.projectId) return;
    loadProjectRoomHistory(reset);
    return;
  }

  if (chatContext.mode === 'project-user') {
    if (!chatContext.projectId || !selectedUserId) return;
    loadUserChatHistory(reset); // project-user still uses user chat route
    return;
  }
}

async function loadProjectRoomHistory(reset = false) {
  if (!chatContext || !chatContext.projectId) return;

  if (reset) {
    chatMessages.innerHTML = '';
    chatCursor = null;
  }

  const url = new URL('/api/project-room/history', window.location.origin);
  url.searchParams.set('projectId', chatContext.projectId);
  url.searchParams.set('roomId', chatContext.roomId || 0);

  if (chatCursor) {
    url.searchParams.set('before', chatCursor);
  }

  const res = await fetch(url.toString(), {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${authToken}`, // 🔑 REQUIRED
      'Accept': 'application/json'
    }
  });

  if (!res.ok) {
    console.error('Project room history failed:', res.status);
    return;
  }

  const data = await res.json();
  if (!data.items) return;

  data.items.forEach(msg => appendMessage(msg));
  chatCursor = data.nextCursor;
}

async function logout() {

  const token = localStorage.getItem('authToken');

  try {

    if (token) {
      await fetch('/logout', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${token}`
        }
      });
    }

  } catch (err) {
    console.warn('Logout cleanup failed (continuing)');
  }

  // ============================
  // CLIENT CLEANUP
  // ============================

  // Remove auth token
  localStorage.removeItem('authToken');

  // Remove cached user info
  localStorage.removeItem('currentUser');
  localStorage.removeItem('scratchPadText');

  // Disconnect socket safely
  if (window.socket && socket.connected) {
    socket.disconnect();
  }

  // Redirect to login
  window.location.href = '/';

}
