import crypto from 'crypto';
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import axios from 'axios';

const TIMEOUT_SECONDS = 1800; // 30 Minuten

class Client {
  constructor(sessionId) {
    this.sessionId = sessionId;
    this.gender = '';
    this.country = '';
    this.isoCountryCode = '';
    this.userName = '';
    this.age = 0;
    this.conversations = {};
    this.lastActivity = new Date();
    this.loginTimeStamp = new Date();
    this.blockedUsers = new Set();
    this.socket = null; // Socket.IO Socket-Objekt
    this.chatAuth = null; // { username, rights: Set<string> }
    this.pendingChatLogin = null; // { step: 'username'|'password', username: string }
  }

  setActivity() {
    this.lastActivity = new Date();
  }

  activitiesTimedOut() {
    const secondsSinceActivity = (new Date() - this.lastActivity) / 1000;
    return secondsSinceActivity > TIMEOUT_SECONDS;
  }

  remainingSecondsToTimeout() {
    const secondsSinceActivity = (new Date() - this.lastActivity) / 1000;
    return Math.max(0, TIMEOUT_SECONDS - secondsSinceActivity);
  }

  currentlyLoggedInSeconds() {
    return Math.floor((new Date() - this.loginTimeStamp) / 1000);
  }

  toJSON() {
    return {
      sessionId: this.sessionId,
      userName: this.userName,
      gender: this.gender,
      country: this.country,
      isoCountryCode: this.isoCountryCode,
      age: this.age
    };
  }
}

const CHAT_USERS_FILE_NAME = 'chat-users.json';
const CHAT_RIGHTS = {
  STAT: 'stat',
  KICK: 'kick'
};

function getLogsDir(__dirname) {
  return join(__dirname, '../logs');
}

function ensureLogsDir(__dirname) {
  const logsDir = getLogsDir(__dirname);
  if (!existsSync(logsDir)) {
    mkdirSync(logsDir, { recursive: true });
  }
  return logsDir;
}

function getChatUsersPath(__dirname) {
  return join(ensureLogsDir(__dirname), CHAT_USERS_FILE_NAME);
}

function sha256(value) {
  return crypto.createHash('sha256').update(value).digest('hex');
}

function ensureChatUsersFile(__dirname) {
  const usersPath = getChatUsersPath(__dirname);
  if (existsSync(usersPath)) {
    return;
  }
  // Security: never create predictable default credentials.
  // Admin users must be configured explicitly in logs/chat-users.json.
  writeFileSync(usersPath, '[]\n', 'utf-8');
  console.warn(
    `[Auth] ${CHAT_USERS_FILE_NAME} wurde neu erstellt. Bitte mindestens einen Admin-User mit Passwort-Hash konfigurieren.`
  );
}

function loadChatUsers(__dirname) {
  ensureChatUsersFile(__dirname);
  const usersPath = getChatUsersPath(__dirname);
  const raw = readFileSync(usersPath, 'utf-8').trim();
  if (!raw) return [];

  let users = [];
  try {
    users = JSON.parse(raw);
  } catch (error) {
    throw new Error(`Ungültige ${CHAT_USERS_FILE_NAME}: ${error.message}`);
  }

  if (!Array.isArray(users)) {
    throw new Error(`${CHAT_USERS_FILE_NAME} muss ein Array sein`);
  }

  return users
    .filter((entry) => entry && typeof entry.username === 'string')
    .map((entry) => ({
      username: entry.username.trim(),
      passwordHash: typeof entry.passwordHash === 'string' ? entry.passwordHash.trim() : '',
      rights: Array.isArray(entry.rights) ? entry.rights.map((r) => String(r).toLowerCase()) : []
    }))
    .filter((entry) => entry.username && entry.passwordHash);
}

function parseLoginRecord(line) {
  const trimmed = line.trim();
  if (!trimmed) return null;
  const parts = trimmed.split(',');
  if (parts.length < 5) return null;

  const timestamp = parts[0].trim();
  const userName = parts[1].trim();
  const gender = parts[parts.length - 1].trim();
  const ageRaw = parts[parts.length - 2].trim();
  const country = parts.slice(2, parts.length - 2).join(',').trim();
  const age = Number.parseInt(ageRaw, 10);

  const date = new Date(timestamp);
  if (!timestamp || !userName || !country || Number.isNaN(age) || Number.isNaN(date.getTime())) {
    return null;
  }

  return {
    timestamp,
    date,
    day: timestamp.slice(0, 10),
    userName,
    country,
    age,
    gender
  };
}

let clients = new Map();
let conversations = new Map(); // Key: "user1:user2" (alphabetisch sortiert)

// Map: Socket-ID -> Express-Session-ID (für Session-Wiederherstellung)
let socketToSessionMap = new Map();

// Exportiere clients Map für Zugriff von außen
export function getClientsMap() {
  return clients;
}

// Exportiere Funktion zum Abrufen der Session-ID für einen Socket
export function getSessionIdForSocket(socketId) {
  return socketToSessionMap.get(socketId);
}

export function extractSessionId(handshakeOrRequest) {
  // Unterstützt sowohl Socket.IO handshake als auch Express request
  const cookies = handshakeOrRequest.headers?.cookie || handshakeOrRequest.cookies || '';
  
  console.log('extractSessionId - Cookies:', cookies);
  
  const sessionMatch = cookies.match(/connect\.sid=([^;]+)/);
  if (sessionMatch) {
    let sessionId = sessionMatch[1];
    
    console.log('extractSessionId - Gefundenes Cookie:', sessionId);
    
    // Express-Session speichert die Session-ID als signierten Wert: s:xxxxx.signature
    // Die tatsächliche Session-ID ist nur xxxxx
    if (sessionId.startsWith('s:')) {
      const parts = sessionId.split('.');
      if (parts.length > 0) {
        sessionId = parts[0].substring(2); // Entferne 's:' Präfix
        console.log('extractSessionId - Bereinigte Session-ID:', sessionId);
      }
    }
    
    return sessionId;
  }
  
  console.log('extractSessionId - Kein connect.sid Cookie gefunden');
  // Fallback: Generiere temporäre UUID
  return crypto.randomUUID();
}

// Exportiere Funktion zum Prüfen des Session-Status
export function getSessionStatus(req) {
  // Verwende req.sessionID von Express-Session
  const sessionId = req.sessionID || extractSessionId(req);
  
  if (!sessionId) {
    return null;
  }
  
  const client = clients.get(sessionId);
  
  if (!client || !client.userName) {
    return null;
  }
  
  return {
    sessionId: client.sessionId,
    userName: client.userName,
    gender: client.gender,
    age: client.age,
    country: client.country,
    isoCountryCode: client.isoCountryCode
  };
}

function getConversationKey(user1, user2) {
  return [user1, user2].sort().join(':');
}

function logClientLogin(client, __dirname) {
  try {
    const logsDir = join(__dirname, '../logs');
    // Erstelle logs-Verzeichnis falls es nicht existiert
    if (!existsSync(logsDir)) {
      try {
        mkdirSync(logsDir, { recursive: true });
        console.log(`[Log] Logs-Verzeichnis erstellt: ${logsDir}`);
      } catch (mkdirError) {
        console.error(`[Log] Fehler beim Erstellen des Logs-Verzeichnisses: ${mkdirError.message}`);
        return; // Beende Funktion, wenn Verzeichnis nicht erstellt werden kann
      }
    }
    const logPath = join(logsDir, 'logins.log');
    const logEntry = `${new Date().toISOString()},${client.userName},${client.country},${client.age},${client.gender}\n`;
    appendFileSync(logPath, logEntry, 'utf-8');
  } catch (error) {
    console.error('Fehler beim Loggen des Logins:', error.message);
  }
}

function checkAndLogStart(__dirname) {
  try {
    const logsDir = join(__dirname, '../logs');
    // Erstelle logs-Verzeichnis falls es nicht existiert
    if (!existsSync(logsDir)) {
      try {
        mkdirSync(logsDir, { recursive: true });
        console.log(`[Log] Logs-Verzeichnis erstellt: ${logsDir}`);
      } catch (mkdirError) {
        console.error(`[Log] Fehler beim Erstellen des Logs-Verzeichnisses: ${mkdirError.message}`);
        return; // Beende Funktion, wenn Verzeichnis nicht erstellt werden kann
      }
    }
    const logPath = join(logsDir, 'starts.log');
    const logEntry = `${new Date().toISOString()}\n`;
    appendFileSync(logPath, logEntry, 'utf-8');
  } catch (error) {
    console.error('Fehler beim Loggen des Starts:', error.message);
  }
}

export function setupBroadcast(io, __dirname) {
  // Länderliste beim Start laden
  let countriesMap = {};
  
  async function downloadCountries() {
    try {
      const response = await axios.get('https://pkgstore.datahub.io/core/country-list/data_csv/data/d7c9d7cfb42cb69f4422dec222dbbaa8/data_csv.csv');
      const lines = response.data.split('\n');
      countriesMap = {};
      
      // Parse CSV-Zeile mit Berücksichtigung von Anführungszeichen
      const parseCSVLine = (line) => {
        const result = [];
        let current = '';
        let inQuotes = false;
        
        for (let i = 0; i < line.length; i++) {
          const char = line[i];
          if (char === '"') {
            // Ignoriere Anführungszeichen, sie werden nicht zum Wert hinzugefügt
            inQuotes = !inQuotes;
          } else if (char === ',' && !inQuotes) {
            result.push(current.trim());
            current = '';
          } else {
            current += char;
          }
        }
        result.push(current.trim());
        return result;
      };
      
      for (let i = 1; i < lines.length; i++) {
        const line = lines[i].trim();
        if (!line) continue;
        
        const [name, code] = parseCSVLine(line);
        if (name && code) {
          // Entferne alle Anführungszeichen (auch am Anfang/Ende)
          const cleanName = name.replace(/^["']+|["']+$/g, '').replace(/["']/g, '').trim();
          const cleanCode = code.replace(/^["']+|["']+$/g, '').replace(/["']/g, '').trim();
          if (cleanName && cleanCode) {
            countriesMap[cleanName] = cleanCode.toLowerCase();
          }
        }
      }
      
      console.log(`Länderliste geladen: ${Object.keys(countriesMap).length} Länder`);
    } catch (error) {
      console.error('Fehler beim Laden der Länderliste:', error);
    }
  }
  
  downloadCountries();
  setInterval(downloadCountries, 24 * 60 * 60 * 1000); // Täglich aktualisieren

  function sendCommandResult(socket, lines, kind = 'info') {
    const payload = Array.isArray(lines) ? lines : [String(lines)];
    socket.emit('commandResult', { lines: payload, kind });
  }

  function sendCommandTable(socket, title, columns, rows) {
    socket.emit('commandTable', {
      title: String(title || 'Ausgabe'),
      columns: Array.isArray(columns) ? columns.map((c) => String(c)) : [],
      rows: Array.isArray(rows) ? rows : []
    });
  }

  function hasRight(client, right) {
    return !!client.chatAuth && client.chatAuth.rights instanceof Set && client.chatAuth.rights.has(right);
  }

  function getLoginsPath() {
    return join(ensureLogsDir(__dirname), 'logins.log');
  }

  function readLoginRecords() {
    const logPath = getLoginsPath();
    if (!existsSync(logPath)) return [];
    const raw = readFileSync(logPath, 'utf-8');
    return raw
      .split('\n')
      .map(parseLoginRecord)
      .filter(Boolean);
  }

  function aggregateTop(items, keySelector, limit = 10) {
    const counts = new Map();
    for (const item of items) {
      const key = keySelector(item);
      counts.set(key, (counts.get(key) || 0) + 1);
    }
    return Array.from(counts.entries())
      .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
      .slice(0, limit);
  }

  function readStartsLogStats() {
    const path = join(ensureLogsDir(__dirname), 'starts.log');
    if (!existsSync(path)) {
      return { count: 0, first: null, last: null };
    }
    const lines = readFileSync(path, 'utf-8')
      .split('\n')
      .map((l) => l.trim())
      .filter(Boolean);
    if (lines.length === 0) {
      return { count: 0, first: null, last: null };
    }
    return { count: lines.length, first: lines[0], last: lines[lines.length - 1] };
  }

  /**
   * Sammelt alle verfügbaren Kennzahlen: Live-Server, starts.log, vollständige Auswertung von logins.log
   * (entspricht inhaltlich den /stat-Unterbefehlen in einer Tabelle).
   */
  function buildFullAllStatsRows(records) {
    const rows = [];
    const push = (a, b) => rows.push([String(a), b == null ? '' : String(b)]);

    const liveOnline = Array.from(clients.values()).filter(
      (c) => c.userName && c.socket && c.socket.connected
    ).length;
    const socketCount =
      io.sockets && io.sockets.sockets && typeof io.sockets.sockets.size === 'number'
        ? io.sockets.sockets.size
        : 0;
    let totalMessages = 0;
    for (const msgs of conversations.values()) {
      totalMessages += Array.isArray(msgs) ? msgs.length : 0;
    }

    const starts = readStartsLogStats();

    push('— Live (Server) —', '');
    push('Nutzer mit Chat-Login und aktiver Verbindung', liveOnline);
    push('Socket.IO-Verbindungen (/)', socketCount);
    push('Client-Sitzungen im Speicher', clients.size);
    push('Unterhaltungen im Speicher (Paare)', conversations.size);
    push('Nachrichten-Einträge gesamt (RAM)', totalMessages);
    push('Server-Starts protokolliert (starts.log)', starts.count);
    if (starts.first) {
      push('Erster Start (Timestamp)', starts.first);
    }
    if (starts.last) {
      push('Letzter Start (Timestamp)', starts.last);
    }

    push('— Login-Protokoll (logins.log, UTC) —', '');
    if (records.length === 0) {
      push('Keine Einträge', '—');
      return rows;
    }

    const sortedByDate = records.slice().sort((a, b) => a.date - b.date);
    const firstTs = sortedByDate[0].timestamp;
    const lastTs = sortedByDate[sortedByDate.length - 1].timestamp;
    const daysSorted = records.map((r) => r.day).sort();
    const dayMin = daysSorted[0];
    const dayMax = daysSorted[daysSorted.length - 1];

    const today = new Date().toISOString().slice(0, 10);
    const dayRecords = records.filter((r) => r.day === today);
    const latestByUserToday = new Map();
    dayRecords
      .slice()
      .sort((a, b) => b.date - a.date)
      .forEach((record) => {
        if (!latestByUserToday.has(record.userName)) {
          latestByUserToday.set(record.userName, record);
        }
      });
    const uniqueToday = latestByUserToday.size;
    const namesToday = Array.from(latestByUserToday.keys()).sort((a, b) => a.localeCompare(b, 'de'));

    const uniqueNames = new Set(records.map((r) => r.userName)).size;
    const ages = records.map((r) => r.age);
    const minAge = Math.min(...ages);
    const maxAge = Math.max(...ages);
    const youngest = records.find((r) => r.age === minAge);
    const oldest = records.find((r) => r.age === maxAge);

    push('Zeitraum (Kalendertage)', `${dayMin} … ${dayMax}`);
    push('Erster Login (Timestamp)', firstTs);
    push('Letzter Login (Timestamp)', lastTs);
    push('Logins gesamt', records.length);
    push(`Logins heute (${today} UTC)`, dayRecords.length);
    push('Verschiedene Nutzer heute (UTC)', uniqueToday);
    const listToday = namesToday.join(', ');
    push(
      'Nutzer heute (Liste)',
      listToday.length > 500
        ? `${uniqueToday} Nutzer — Auszug: ${namesToday.slice(0, 8).join(', ')} … (vollständig: /stat today)`
        : listToday || '—'
    );
    push('Verschiedene Namen (historisch)', uniqueNames);
    push('Ø Logins pro Namen (historisch)', (records.length / uniqueNames).toFixed(2));
    push('Jüngster Nutzer (erster Treffer im Log)', `${youngest.userName} (${youngest.age})`);
    push('Ältester Nutzer (erster Treffer im Log)', `${oldest.userName} (${oldest.age})`);

    const topGenders = aggregateTop(
      records,
      (r) => (r.gender || '').trim() || '?',
      15
    );
    push('Geschlecht (Häufigkeit)', topGenders.map(([g, n]) => `${g}(${n})`).join(', ') || '—');

    const topCountries = aggregateTop(records, (r) => r.country, 20);
    push('Top 20 Länder', topCountries.map(([c, n]) => `${c}(${n})`).join(', ') || '—');

    const topNames = aggregateTop(records, (r) => r.userName, 20);
    const topNamesDetail = topNames
      .map(([name, count]) => {
        const latestRecord = records
          .filter((r) => r.userName === name)
          .sort((a, b) => b.date - a.date)[0];
        return `${name}(${count}, zuletzt ${latestRecord.timestamp})`;
      })
      .join('; ');
    push(
      'Top 20 Namen (Anzahl, letzter Login)',
      topNamesDetail.length > 1200 ? `${topNamesDetail.slice(0, 1197)}…` : topNamesDetail
    );
    push('Hinweis', 'Tabellen je Tag/Zeitraum: /stat today | /stat date | /stat range');

    return rows;
  }

  function executeStatsCommand(socket, client, parts) {
    if (!hasRight(client, CHAT_RIGHTS.STAT)) {
      sendCommandResult(socket, 'Keine Berechtigung: Recht "stat" fehlt.');
      return;
    }

    const records = readLoginRecords();
    if (records.length === 0) {
      sendCommandResult(socket, 'Keine Login-Daten vorhanden.');
      return;
    }

    const sub = (parts[1] || '').toLowerCase();

    if (!sub || sub === 'help') {
      sendCommandTable(socket, 'Hilfe: Statistik-Befehle', ['Befehl', 'Beschreibung'], [
        ['/stat today', 'Logins des heutigen Tages'],
        ['/stat date YYYY-MM-DD', 'Logins an einem bestimmten Datum'],
        ['/stat range YYYY-MM-DD YYYY-MM-DD', 'Logins pro Tag im Zeitraum'],
        ['/stat ages', 'Jüngster und ältester Nutzer'],
        ['/stat names', 'Häufigkeit der verwendeten Namen'],
        ['/stat countries', 'Häufigkeit der Länder'],
        ['/all-stats', 'Alle Kennzahlen: Live-Server + starts.log + komplette logins.log-Auswertung']
      ]);
      return;
    }

    if (sub === 'today') {
      const day = new Date().toISOString().slice(0, 10);
      const dayRecords = records.filter((r) => r.day === day);
      const latestByUserToday = new Map();
      dayRecords
        .slice()
        .sort((a, b) => b.date - a.date)
        .forEach((record) => {
          if (!latestByUserToday.has(record.userName)) {
            latestByUserToday.set(record.userName, record);
          }
        });
      const uniqueUsersToday = Array.from(latestByUserToday.values())
        .sort((a, b) => a.userName.localeCompare(b.userName, 'de'))
        .map((r) => `${r.userName} (${r.age})`);
      sendCommandTable(socket, 'Statistik: Heute', ['Metrik', 'Wert'], [
        ['Tag', day],
        ['Logins', dayRecords.length],
        ['Heute eingeloggt', uniqueUsersToday.length > 0 ? uniqueUsersToday.join(', ') : '-']
      ]);
      return;
    }

    if (sub === 'date') {
      const day = parts[2];
      if (!day || !/^\d{4}-\d{2}-\d{2}$/.test(day)) {
        sendCommandResult(socket, 'Nutzung: /stat date YYYY-MM-DD');
        return;
      }
      const dayRecords = records.filter((r) => r.day === day);
      sendCommandTable(socket, 'Statistik: Datum', ['Tag', 'Logins'], [[day, dayRecords.length]]);
      return;
    }

    if (sub === 'range') {
      const from = parts[2];
      const to = parts[3];
      if (!from || !to || !/^\d{4}-\d{2}-\d{2}$/.test(from) || !/^\d{4}-\d{2}-\d{2}$/.test(to)) {
        sendCommandResult(socket, 'Nutzung: /stat range YYYY-MM-DD YYYY-MM-DD');
        return;
      }
      const filtered = records.filter((r) => r.day >= from && r.day <= to);
      const perDay = aggregateTop(filtered, (r) => r.day, 1000)
        .sort((a, b) => a[0].localeCompare(b[0]))
        .map(([day, count]) => [day, count]);
      sendCommandTable(socket, `Statistik: Zeitraum ${from} bis ${to}`, ['Tag', 'Logins'], perDay);
      return;
    }

    if (sub === 'ages') {
      const ages = records.map((r) => r.age);
      const minAge = Math.min(...ages);
      const maxAge = Math.max(...ages);
      const youngest = records.find((r) => r.age === minAge);
      const oldest = records.find((r) => r.age === maxAge);
      sendCommandTable(socket, 'Statistik: Alter', ['Kategorie', 'Name', 'Alter'], [
        ['Jüngster Nutzer', youngest.userName, youngest.age],
        ['Ältester Nutzer', oldest.userName, oldest.age]
      ]);
      return;
    }

    if (sub === 'names') {
      const topNames = aggregateTop(records, (r) => r.userName, 20);
      sendCommandTable(
        socket,
        `Statistik: Namen (gesamt verschieden: ${new Set(records.map((r) => r.userName)).size})`,
        ['Name', 'Alter', 'Anzahl', 'Letzter Login'],
        topNames.map(([name, count]) => {
          const latestRecord = records
            .filter((r) => r.userName === name)
            .sort((a, b) => b.date - a.date)[0];
          return [name, latestRecord ? latestRecord.age : '-', count, latestRecord ? latestRecord.timestamp : '-'];
        })
      );
      return;
    }

    if (sub === 'countries') {
      const topCountries = aggregateTop(records, (r) => r.country, 20);
      sendCommandTable(
        socket,
        'Statistik: Länder',
        ['Land', 'Anzahl'],
        topCountries.map(([country, count]) => [country, count])
      );
      return;
    }

    sendCommandResult(socket, 'Unbekannter /stat-Befehl. Nutze /stat help');
  }

  function executeAllStatsCommand(socket, client) {
    if (!hasRight(client, CHAT_RIGHTS.STAT)) {
      sendCommandResult(socket, 'Keine Berechtigung: Recht "stat" fehlt.');
      return;
    }
    const rows = buildFullAllStatsRows(readLoginRecords());
    sendCommandTable(socket, 'Statistik: Vollständige Übersicht (/all-stats)', ['Metrik', 'Wert'], rows);
  }

  function executeKickCommand(socket, client, parts) {
    if (!hasRight(client, CHAT_RIGHTS.KICK)) {
      sendCommandResult(socket, 'Keine Berechtigung: Recht "kick" fehlt.');
      return;
    }

    const targetName = (parts[1] || '').trim();
    if (!targetName) {
      sendCommandResult(socket, 'Nutzung: /kick <username>');
      return;
    }

    let targetSessionId = null;
    let targetClient = null;
    for (const [sid, c] of clients.entries()) {
      if (c.userName === targetName && c.socket && c.socket.connected) {
        targetSessionId = sid;
        targetClient = c;
        break;
      }
    }

    if (!targetClient) {
      sendCommandResult(socket, `User "${targetName}" ist nicht online.`);
      return;
    }

    if (targetClient.socket) {
      targetClient.socket.emit('error', { message: 'Du wurdest vom Chat getrennt (kick).' });
      targetClient.socket.disconnect(true);
    }
    clients.delete(targetSessionId);
    broadcastUserList();
    sendCommandResult(socket, `User "${targetName}" wurde gekickt.`);
  }

  function verifyChatUser(username, password) {
    const users = loadChatUsers(__dirname);
    const user = users.find((u) => u.username.toLowerCase() === username.toLowerCase());
    if (!user) return null;

    const [algo, hash] = user.passwordHash.split(':');
    if (algo !== 'sha256' || !hash) return null;
    const inputHash = sha256(password);
    if (inputHash !== hash) return null;

    return {
      username: user.username,
      rights: new Set(user.rights)
    };
  }

  function executeCommand(socket, client, rawInput) {
    const input = rawInput.trim();

    // Laufender Login-Dialog: Nur Eingaben ohne Slash als Username/Passwort behandeln.
    if (client.pendingChatLogin && !input.startsWith('/')) {
      if (client.pendingChatLogin.step === 'username') {
        const enteredUser = input;
        if (!enteredUser) {
          sendCommandResult(socket, 'Username darf nicht leer sein. Bitte Username eingeben:', 'loginPromptUsername');
          return true;
        }
        client.pendingChatLogin = { step: 'password', username: enteredUser };
        sendCommandResult(socket, 'Passwort eingeben:', 'loginPromptPassword');
        return true;
      }

      if (client.pendingChatLogin.step === 'password') {
        const username = client.pendingChatLogin.username;
        const auth = verifyChatUser(username, input);
        client.pendingChatLogin = null;
        if (!auth) {
          sendCommandResult(socket, 'Login fehlgeschlagen. Benutzername oder Passwort falsch.', 'loginError');
          return true;
        }

        client.chatAuth = auth;
        sendCommandResult(
          socket,
          `Login erfolgreich als ${auth.username}. Rechte: ${Array.from(auth.rights).join(', ') || 'keine'}`,
          'loginSuccess'
        );
        return true;
      }
    } else if (client.pendingChatLogin && input.startsWith('/')) {
      // Ein neuer /Befehl bricht den Login-Vorgang ab.
      client.pendingChatLogin = null;
      sendCommandResult(socket, 'Login-Vorgang abgebrochen.', 'loginAbort');
      // und läuft unten als normaler Befehl weiter
    }

    if (!input.startsWith('/')) return false;

    const parts = input.split(/\s+/);
    const command = parts[0].toLowerCase();

    if (command === '/login') {
      const username = (parts[1] || '').trim();
      if (username) {
        client.pendingChatLogin = { step: 'password', username };
        sendCommandResult(socket, 'Passwort eingeben:', 'loginPromptPassword');
      } else {
        client.pendingChatLogin = { step: 'username', username: '' };
        sendCommandResult(socket, 'Username eingeben:', 'loginPromptUsername');
      }
      return true;
    }

    if (command === '/logout-admin') {
      const wasLoggedIn = !!client.chatAuth;
      client.chatAuth = null;
      client.pendingChatLogin = null;
      sendCommandResult(
        socket,
        wasLoggedIn
          ? 'Admin/Command-Login wurde abgemeldet.'
          : 'Es war kein Admin/Command-Login aktiv.',
        'loginLogout'
      );
      return true;
    }

    if (command === '/whoami-rights') {
      if (!client.chatAuth) {
        sendCommandResult(socket, 'Nicht per Command-Login angemeldet.', 'whoami');
        return true;
      }
      sendCommandResult(socket, [
        `Angemeldet als: ${client.chatAuth.username}`,
        `Rechte: ${Array.from(client.chatAuth.rights).join(', ') || 'keine'}`
      ], 'whoami');
      return true;
    }

    if (command === '/help' || command === '/?') {
      sendCommandTable(socket, 'Hilfe: Verfügbare Befehle', ['Befehl', 'Beschreibung'], [
        ['/login [username]', 'Admin-/Command-Login starten'],
        ['/logout-admin', 'Admin-/Command-Login beenden'],
        ['/whoami-rights', 'Aktuelle Admin-Rechte anzeigen'],
        ['/stat help', 'Hilfe zu Statistikbefehlen anzeigen'],
        ['/all-stats', 'Alle Kennzahlen: Live-Server + starts.log + komplette logins.log-Auswertung'],
        ['/kick <username>', 'Benutzer aus dem Chat werfen'],
        ['/help oder /?', 'Diese Hilfe anzeigen']
      ]);
      return true;
    }

    if (command === '/stat') {
      executeStatsCommand(socket, client, parts);
      return true;
    }

    if (command === '/all-stats') {
      executeAllStatsCommand(socket, client);
      return true;
    }

    if (command === '/kick') {
      executeKickCommand(socket, client, parts);
      return true;
    }

    sendCommandResult(socket, `Unbekannter Befehl: ${command}`, 'unknown');
    return true;
  }

  // Socket.IO-Verbindungshandler
  io.on('connection', (socket) => {
    const request = socket.handshake;
    
    // Versuche Session-ID aus Cookie zu extrahieren
    let sessionId = extractSessionId(request);
    
    console.log('Socket.IO Connect - Session-ID (aus Cookie):', sessionId);
    console.log('Socket.IO Connect - Cookie:', request.headers?.cookie);
    
    // Wenn keine gültige Session-ID gefunden wurde, generiere eine temporäre UUID
    // Diese wird beim Login durch die Express-Session-ID ersetzt (siehe handleLogin)
    if (!sessionId || sessionId.length < 10 || !sessionId.match(/^[a-zA-Z0-9_-]+$/)) {
      sessionId = crypto.randomUUID();
      console.log('Socket.IO Connect - Keine gültige Session-ID gefunden, verwende temporäre UUID:', sessionId);
      socket.data.temporarySessionId = true; // Markiere als temporär
    }
    
    // Speichere Socket-ID mit Session-ID
    socket.data.sessionId = sessionId;
    
    let client = clients.get(sessionId);
    if (!client) {
      console.log('Socket.IO Connect - Neuer Client erstellt für Session-ID:', sessionId);
      client = new Client(sessionId);
      clients.set(sessionId, client);
    } else {
      console.log('Socket.IO Connect - Bestehender Client gefunden für Session-ID:', sessionId, 'User:', client.userName);
    }
    
    // Speichere Socket mit Session-ID
    client.socket = socket;
    
    // Bestätigung an Client senden, inklusive Benutzerdaten falls bereits eingeloggt
    const connectedData = { sessionId };
    if (client.userName) {
      connectedData.user = {
        userName: client.userName,
        gender: client.gender,
        age: client.age,
        country: client.country,
        isoCountryCode: client.isoCountryCode
      };
      connectedData.loggedIn = true;
    }
    socket.emit('connected', connectedData);

    socket.on('disconnect', (reason) => {
      console.log(`[Disconnect] Socket getrennt für Session-ID: ${sessionId}, Grund: ${reason}`);
      const client = clients.get(sessionId);
      if (client) {
        console.log(`[Disconnect] Client gefunden: ${client.userName || 'unbekannt'}, Socket war verbunden: ${client.socket ? client.socket.connected : 'null'}`);
        
        // Setze Socket auf null, damit keine Nachrichten mehr an diesen Client gesendet werden
        // ABER: Lösche den Client NICHT, damit die Session beim Reload wiederhergestellt werden kann
        client.socket = null;
        
        // Aktualisiere Benutzerliste, damit andere Clients sehen, dass dieser Benutzer offline ist
        if (client.userName) {
          console.log(`[Disconnect] Aktualisiere Benutzerliste nach Disconnect von ${client.userName}`);
          broadcastUserList();
        }
        // Client bleibt in der Map, damit Session-Wiederherstellung funktioniert
      } else {
        console.log(`[Disconnect] Kein Client gefunden für Session-ID: ${sessionId}`);
      }
    });

    socket.on('error', (error) => {
      console.error('Socket.IO-Fehler:', error);
    });

    // Socket.IO Event-Handler: Setze Express-Session-ID
    socket.on('setSessionId', (data) => {
      const { expressSessionId } = data;
      if (expressSessionId) {
        console.log('setSessionId - Express-Session-ID erhalten:', expressSessionId);
        const currentSessionId = socket.data.sessionId;
        
        if (currentSessionId !== expressSessionId) {
          console.log('setSessionId - Aktualisiere Session-ID von', currentSessionId, 'zu', expressSessionId);
          
          // Prüfe, ob bereits ein Client mit dieser Session-ID existiert
          let existingClient = clients.get(expressSessionId);
          
          // Wenn kein Client mit dieser Session-ID gefunden wurde, prüfe ob ein Client mit dem aktuellen Socket existiert
          // und bereits eingeloggt ist (für Session-Wiederherstellung nach Reload)
          if (!existingClient) {
            const currentClient = clients.get(currentSessionId);
            if (currentClient && currentClient.userName) {
              console.log('setSessionId - Client mit Session-ID', currentSessionId, 'ist bereits eingeloggt:', currentClient.userName);
              // Verwende bestehenden Client und aktualisiere nur die Session-ID
              existingClient = currentClient;
              clients.delete(currentSessionId);
              clients.set(expressSessionId, existingClient);
              existingClient.sessionId = expressSessionId;
            } else {
              // Wenn kein Client mit der aktuellen Session-ID gefunden wurde, suche nach einem Client,
              // der bereits eingeloggt ist und keinen aktiven Socket hat (für Session-Wiederherstellung)
              // Dies ist ein Fallback für den Fall, dass die Session-ID beim Reload geändert wurde
              for (const [sid, c] of clients.entries()) {
                if (c.userName && (!c.socket || !c.socket.connected)) {
                  console.log('setSessionId - Gefundener Client ohne aktiven Socket:', sid, c.userName);
                  // Verwende diesen Client und aktualisiere die Session-ID
                  existingClient = c;
                  clients.delete(sid);
                  clients.set(expressSessionId, existingClient);
                  existingClient.sessionId = expressSessionId;
                  break;
                }
              }
            }
          }
          
          if (existingClient) {
            // Verwende bestehenden Client
            console.log('setSessionId - Verwende bestehenden Client, User:', existingClient.userName || 'nicht eingeloggt');
            existingClient.socket = socket;
            socket.data.sessionId = expressSessionId;
            
            // Entferne alten Client, falls unterschiedlich
            if (currentSessionId !== expressSessionId && clients.has(currentSessionId)) {
              clients.delete(currentSessionId);
            }
            
            // Sende Login-Status zurück, falls bereits eingeloggt
            if (existingClient.userName) {
              console.log('setSessionId - Sende Login-Status zurück für User:', existingClient.userName);
              socket.emit('connected', {
                sessionId: expressSessionId,
                loggedIn: true,
                user: existingClient.toJSON()
              });
              
              // Aktualisiere Userliste für alle Clients, damit der wiederhergestellte Client die Liste erhält
              broadcastUserList();
            }
          } else {
            // Erstelle neuen Client mit Express-Session-ID
            console.log('setSessionId - Erstelle neuen Client');
            const newClient = new Client(expressSessionId);
            newClient.socket = socket;
            clients.set(expressSessionId, newClient);
            socket.data.sessionId = expressSessionId;
            
            // Entferne alten Client
            if (clients.has(currentSessionId)) {
              clients.delete(currentSessionId);
            }
          }
        }
      }
    });

    // Socket.IO Event-Handler
    // WICHTIG: Hole den Client immer dynamisch basierend auf socket.data.sessionId,
    // da sich die Session-ID nach setSessionId ändern kann
    socket.on('login', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        await handleLogin(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der Login-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('message', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleMessage(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der Message-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('requestConversation', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleRequestConversation(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der RequestConversation-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('userSearch', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleUserSearch(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der UserSearch-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('requestHistory', async () => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleRequestHistory(socket, currentClient);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der RequestHistory-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('requestOpenConversations', async () => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleRequestOpenConversations(socket, currentClient);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der RequestOpenConversations-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('blockUser', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleBlockUser(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der BlockUser-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

    socket.on('unblockUser', async (data) => {
      try {
        const currentClient = clients.get(socket.data.sessionId);
        if (!currentClient) {
          socket.emit('error', { message: 'Client nicht gefunden' });
          return;
        }
        currentClient.setActivity();
        handleUnblockUser(socket, currentClient, data);
      } catch (error) {
        console.error('Fehler beim Verarbeiten der UnblockUser-Nachricht:', error);
        socket.emit('error', { message: error.message });
      }
    });

  });

  async function handleLogin(socket, client, data) {
    const { userName, gender, age, country, expressSessionId } = data;
    
    // Validierung
    if (!userName || userName.trim().length < 3) {
      socket.emit('error', { message: 'Benutzername muss mindestens 3 Zeichen lang sein' });
      return;
    }
    
    // Wenn eine Express-Session-ID übergeben wurde, verwende diese statt der Socket-ID
    if (expressSessionId) {
      console.log('handleLogin - Express-Session-ID erhalten:', expressSessionId);
      
      // Entferne alten Client mit temporärer Socket-ID, falls vorhanden
      const oldSessionId = socket.data.sessionId;
      if (oldSessionId && oldSessionId !== expressSessionId && clients.has(oldSessionId)) {
        console.log('handleLogin - Entferne alten Client mit Session-ID:', oldSessionId);
        clients.delete(oldSessionId);
      }
      
      // Prüfe, ob bereits ein Client mit dieser Session-ID existiert
      let existingClient = clients.get(expressSessionId);
      if (existingClient) {
        // Verwende bestehenden Client
        console.log('handleLogin - Verwende bestehenden Client mit Session-ID:', expressSessionId);
        client = existingClient;
        // Aktualisiere Socket-Verbindung
        client.socket = socket;
        socket.data.sessionId = expressSessionId;
      } else {
        // Erstelle neuen Client mit Express-Session-ID
        console.log('handleLogin - Erstelle neuen Client mit Session-ID:', expressSessionId);
        client = new Client(expressSessionId);
        clients.set(expressSessionId, client);
        client.socket = socket;
        socket.data.sessionId = expressSessionId;
      }
    } else {
      console.log('handleLogin - Keine Express-Session-ID erhalten, verwende Socket-ID');
    }
    
    // Prüfe, ob Name bereits verwendet wird
    for (const [sid, c] of clients.entries()) {
      if (c.userName === userName && sid !== client.sessionId) {
        socket.emit('error', { message: 'Dieser Benutzername ist bereits vergeben' });
        return;
      }
    }
    
    client.userName = userName.trim();
    client.gender = gender;
    client.age = age;
    client.country = country;
    
    // ISO-Code aus Länderliste ermitteln
    client.isoCountryCode = countriesMap[country] || 'unknown';
    
    client.loginTimeStamp = new Date();
    client.setActivity();
    
    // Stelle sicher, dass der Socket gesetzt ist
    if (!client.socket) {
      console.log('handleLogin - WARNUNG: Socket nicht gesetzt, setze ihn jetzt');
      client.socket = socket;
    }
    
    console.log('handleLogin - Client nach Login:', {
      sessionId: client.sessionId,
      userName: client.userName,
      hasSocket: !!client.socket,
      socketConnected: client.socket ? client.socket.connected : false
    });
    
    logClientLogin(client, __dirname);
    checkAndLogStart(__dirname);
    
    // Benutzerliste an alle senden
    broadcastUserList();
    
    // Aktualisiere ungelesene Nachrichten für den neuen Benutzer
    updateUnreadCount(client);
    
    console.log('handleLogin - Sende loginSuccess an Socket');
    socket.emit('loginSuccess', { 
      sessionId: client.sessionId,
      user: client.toJSON()
    });
  }

  function handleMessage(socket, client, data) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const { toUserName, message, messageId, isImage, imageType, imageUrl } = data;

    // Chat-Befehle werden direkt serverseitig verarbeitet
    if (!isImage && typeof message === 'string' && executeCommand(socket, client, message)) {
      return;
    }
    
    if (!toUserName) {
      socket.emit('error', { message: 'Empfänger fehlt' });
      return;
    }
    
    // Finde Empfänger
    let receiver = null;
    for (const [sid, c] of clients.entries()) {
      if (c.userName === toUserName) {
        receiver = c;
        break;
      }
    }
    
    if (!receiver) {
      socket.emit('error', { message: 'Benutzer nicht gefunden' });
      return;
    }
    
    // Prüfe, ob Empfänger noch verbunden ist
    if (!receiver.socket || !receiver.socket.connected) {
      // Empfänger ist nicht mehr verbunden, entferne ihn aus der Liste
      clients.delete(receiver.sessionId);
      broadcastUserList();
      socket.emit('error', { message: 'Benutzer nicht gefunden' });
      return;
    }
    
    // Prüfe Blockierung
    if (receiver.blockedUsers.has(client.userName)) {
      socket.emit('error', { message: 'Du wurdest von diesem Benutzer blockiert' });
      return;
    }
    
    // Speichere Nachricht in Konversation
    const convKey = getConversationKey(client.userName, toUserName);
    if (!conversations.has(convKey)) {
      conversations.set(convKey, []);
    }
    
    const conversation = conversations.get(convKey);
    conversation.push({
      from: client.userName,
      to: toUserName,
      message: isImage && imageUrl ? imageUrl : message, // Verwende URL für Bilder
      messageId,
      timestamp: new Date().toISOString(),
      read: false,
      isImage: isImage || false,
      imageType: imageType || null,
      imageUrl: imageUrl || null,
      imageCode: isImage ? message : null
    });
    
    // Sende an Empfänger (wenn online)
    const messagePayload = {
      from: client.userName,
      message: isImage && imageUrl ? imageUrl : message, // Verwende URL für Bilder
      messageId,
      timestamp: new Date().toISOString(),
      isImage: isImage || false,
      imageType: imageType || null,
      imageUrl: imageUrl || null,
      imageCode: isImage ? message : null // Code für Server-Referenz
    };
    
    // Debug-Logging für Bilder
    if (isImage) {
      console.log(`[Bild] Sende Bild von ${client.userName} an ${toUserName}, Größe: ${message ? message.length : 0} Zeichen, Typ: ${imageType || 'unbekannt'}`);
      console.log(`[Bild] Absender Socket verbunden: ${socket.connected}, Empfänger Socket verbunden: ${receiver.socket ? receiver.socket.connected : 'null'}`);
    }
    
    // Prüfe, ob Absender noch verbunden ist
    if (!socket.connected) {
      console.error(`[Bild] Absender ${client.userName} Socket nicht mehr verbunden beim Senden!`);
      return;
    }
    
    // Prüfe, ob Empfänger-Socket noch existiert und verbunden ist
    if (!receiver.socket || !receiver.socket.connected) {
      console.error(`[Bild] Empfänger ${toUserName} Socket nicht mehr verbunden beim Senden!`);
      socket.emit('error', { message: 'Empfänger ist nicht mehr online' });
      return;
    }
    
    try {
      receiver.socket.emit('message', messagePayload);
      
      // Bestätigung an Absender (nur wenn noch verbunden)
      if (socket.connected) {
        socket.emit('messageSent', { 
          messageId,
          to: toUserName
        });
      }
    } catch (error) {
      console.error(`[Bild] Fehler beim Senden der Nachricht:`, error);
      if (socket.connected) {
        socket.emit('error', { message: 'Fehler beim Senden der Nachricht' });
      }
    }
    
    // Aktualisiere ungelesene Nachrichten für den Empfänger
    updateUnreadCount(receiver);
  }

  function handleRequestConversation(socket, client, data) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const { withUserName } = data;
    const convKey = getConversationKey(client.userName, withUserName);
    const conversation = conversations.get(convKey) || [];
    
    // Markiere alle Nachrichten von diesem Benutzer als gelesen
    for (const msg of conversation) {
      if (msg.to === client.userName && msg.from === withUserName) {
        msg.read = true;
      }
    }
    
    // Aktualisiere ungelesene Nachrichten nach dem Markieren als gelesen
    updateUnreadCount(client);
    
    socket.emit('conversation', {
      with: withUserName,
      messages: conversation.map(msg => ({
        from: msg.from,
        message: msg.message,
        timestamp: msg.timestamp,
        isImage: msg.isImage || false,
        imageType: msg.imageType || null
      }))
    });
  }

  function handleUserSearch(socket, client, data) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const { nameIncludes, minAge, maxAge, countries, genders } = data;
    
    const results = [];
    for (const [sid, c] of clients.entries()) {
      if (!c.userName || c.userName === client.userName) continue;
      
      // Name-Filter
      if (nameIncludes && !c.userName.toLowerCase().includes(nameIncludes.toLowerCase())) {
        continue;
      }
      
      // Alter-Filter
      if (minAge && c.age < minAge) continue;
      if (maxAge && c.age > maxAge) continue;
      
      // Länder-Filter
      if (countries && countries.length > 0 && !countries.includes(c.country)) {
        continue;
      }
      
      // Geschlecht-Filter
      if (genders && genders.length > 0 && !genders.includes(c.gender)) {
        continue;
      }
      
      results.push(c.toJSON());
    }
    
    socket.emit('searchResults', {
      results
    });
  }

  function handleRequestHistory(socket, client) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const history = [];
    for (const [convKey, messages] of conversations.entries()) {
      const [user1, user2] = convKey.split(':');
      if (user1 === client.userName || user2 === client.userName) {
        const otherUser = user1 === client.userName ? user2 : user1;
        history.push({
          userName: otherUser,
          lastMessage: messages.length > 0 ? messages[messages.length - 1] : null
        });
      }
    }
    
    socket.emit('historyResults', {
      results: history
    });
  }

  function handleRequestOpenConversations(socket, client) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const inbox = [];
    for (const [convKey, messages] of conversations.entries()) {
      const [user1, user2] = convKey.split(':');
      if (user1 === client.userName || user2 === client.userName) {
        const otherUser = user1 === client.userName ? user2 : user1;
        const unreadCount = messages.filter(m => 
          m.to === client.userName && !m.read
        ).length;
        
        if (unreadCount > 0) {
          inbox.push({
            userName: otherUser,
            unreadCount
          });
        }
      }
    }
    
    socket.emit('inboxResults', {
      results: inbox
    });
  }

  function handleBlockUser(socket, client, data) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const { userName } = data;
    client.blockedUsers.add(userName);
    
    socket.emit('userBlocked', {
      userName
    });
  }

  function handleUnblockUser(socket, client, data) {
    if (!client.userName) {
      socket.emit('error', { message: 'Nicht eingeloggt' });
      return;
    }
    
    const { userName } = data;
    client.blockedUsers.delete(userName);
    
    socket.emit('userUnblocked', {
      userName
    });
  }

  function updateUnreadCount(client) {
    if (!client.userName || !client.socket || !client.socket.connected) {
      return;
    }
    
    // Zähle Personen mit ungelesenen Nachrichten für diesen Benutzer
    let unreadPersons = 0;
    for (const [convKey, messages] of conversations.entries()) {
      const [user1, user2] = convKey.split(':');
      if (user1 === client.userName || user2 === client.userName) {
        const unreadCount = messages.filter(m => 
          m.to === client.userName && !m.read
        ).length;
        if (unreadCount > 0) {
          unreadPersons++;
        }
      }
    }
    
    // Sende Update an Client
    client.socket.emit('unreadChats', {
      count: unreadPersons
    });
  }

  function broadcastUserList() {
    // Filtere nur eingeloggte Benutzer mit aktiver Verbindung
    const userList = Array.from(clients.values())
      .filter(c => {
        const hasUserName = !!c.userName;
        const hasSocket = !!c.socket;
        const isConnected = c.socket && c.socket.connected;
        if (hasUserName && (!hasSocket || !isConnected)) {
          console.log('broadcastUserList - Client ohne Socket oder nicht verbunden:', c.userName, 'socket:', hasSocket, 'connected:', isConnected);
        }
        return hasUserName && hasSocket && isConnected;
      })
      .map(c => c.toJSON());
    
    console.log('broadcastUserList - Sende Userliste mit', userList.length, 'Benutzern:', userList.map(u => u.userName));
    
    // Sende an alle verbundenen Clients
    io.emit('userList', {
      users: userList
    });
  }

  function broadcastToUser(userName, data) {
    // Finde den Client mit diesem Benutzernamen und sende die Nachricht
    for (const [sid, c] of clients.entries()) {
      if (c.userName === userName && c.socket) {
        c.socket.emit(data.type, data);
        return;
      }
    }
  }

  // Timeout-Check alle 60 Sekunden
  setInterval(() => {
    for (const [sid, client] of clients.entries()) {
      if (client.activitiesTimedOut()) {
        console.log(`Client ${client.userName} hat Timeout erreicht`);
        clients.delete(sid);
        broadcastUserList();
      }
    }
  }, 60000);
}
