/**
 * TASKMATE - Auth Routes
 * ======================
 * Login, Logout, Token-Refresh
 */

const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const { getDb } = require('../database');
const { generateAccessToken, generateRefreshToken, refreshAccessToken, revokeAllRefreshTokens, authenticateToken } = require('../middleware/auth');
const { getTokenForUser } = require('../middleware/csrf');
const { validatePassword } = require('../middleware/validation');
const logger = require('../utils/logger');

// Konfiguration
const MAX_LOGIN_ATTEMPTS = parseInt(process.env.MAX_LOGIN_ATTEMPTS) || 5;
const LOCKOUT_DURATION = (parseInt(process.env.LOCKOUT_DURATION_MINUTES) || 15) * 60 * 1000;

/**
 * POST /api/auth/login
 * Benutzer anmelden
 * - Admin-User loggt sich mit username "admin" ein
 * - Alle anderen User loggen sich mit E-Mail ein
 */
router.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    const ip = req.ip || req.connection.remoteAddress;
    const userAgent = req.headers['user-agent'];

    if (!username || !password) {
      return res.status(400).json({ error: 'E-Mail/Benutzername und Passwort erforderlich' });
    }

    const db = getDb();

    // Benutzer suchen: Admin kann mit "admin" einloggen, alle anderen mit E-Mail
    let user;
    if (username.toLowerCase() === 'admin') {
      // Admin-Login über spezielle E-Mail
      user = db.prepare('SELECT * FROM users WHERE email = ? AND role = ?').get('admin@taskmate.local', 'admin');
    } else {
      // Normale User loggen sich mit E-Mail ein
      user = db.prepare('SELECT * FROM users WHERE email = ?').get(username);
    }

    // Audit-Log Eintrag vorbereiten
    const logAttempt = (userId, success) => {
      db.prepare(`
        INSERT INTO login_audit (user_id, ip_address, success, user_agent)
        VALUES (?, ?, ?, ?)
      `).run(userId, ip, success ? 1 : 0, userAgent);
    };

    if (!user) {
      logger.warn(`Login fehlgeschlagen: Benutzer nicht gefunden - ${username}`);
      return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
    }

    // Prüfen ob Account gesperrt ist
    if (user.locked_until) {
      const lockedUntil = new Date(user.locked_until).getTime();
      if (Date.now() < lockedUntil) {
        const remainingMinutes = Math.ceil((lockedUntil - Date.now()) / 60000);
        logger.warn(`Login blockiert: Account gesperrt - ${username}`);
        return res.status(423).json({
          error: `Account ist gesperrt. Versuche es in ${remainingMinutes} Minuten erneut.`
        });
      } else {
        // Sperre aufheben
        db.prepare('UPDATE users SET locked_until = NULL, failed_attempts = 0 WHERE id = ?')
          .run(user.id);
      }
    }

    // Passwort prüfen
    const validPassword = await bcrypt.compare(password, user.password_hash);

    if (!validPassword) {
      // Fehlversuche erhöhen
      const newFailedAttempts = (user.failed_attempts || 0) + 1;

      if (newFailedAttempts >= MAX_LOGIN_ATTEMPTS) {
        // Account sperren
        const lockUntil = new Date(Date.now() + LOCKOUT_DURATION).toISOString();
        db.prepare('UPDATE users SET failed_attempts = ?, locked_until = ? WHERE id = ?')
          .run(newFailedAttempts, lockUntil, user.id);
        logger.warn(`Account gesperrt nach ${MAX_LOGIN_ATTEMPTS} Fehlversuchen: ${username}`);
      } else {
        db.prepare('UPDATE users SET failed_attempts = ? WHERE id = ?')
          .run(newFailedAttempts, user.id);
      }

      logAttempt(user.id, false);
      logger.warn(`Login fehlgeschlagen: Falsches Passwort - ${username} (Versuch ${newFailedAttempts})`);

      const remainingAttempts = MAX_LOGIN_ATTEMPTS - newFailedAttempts;
      return res.status(401).json({
        error: 'Ungültige Anmeldedaten',
        remainingAttempts: remainingAttempts > 0 ? remainingAttempts : 0
      });
    }

    // Login erfolgreich - Fehlversuche zurücksetzen
    db.prepare(`
      UPDATE users
      SET failed_attempts = 0, locked_until = NULL, last_login = CURRENT_TIMESTAMP
      WHERE id = ?
    `).run(user.id);

    logAttempt(user.id, true);

    // JWT Access-Token generieren (kurze Lebensdauer)
    const accessToken = generateAccessToken(user);
    
    // Refresh-Token generieren (lange Lebensdauer)
    const refreshToken = generateRefreshToken(user.id, ip, userAgent);

    // CSRF-Token generieren
    const csrfToken = getTokenForUser(user.id);

    logger.info(`Login erfolgreich: ${username}`);

    // Permissions parsen
    let permissions = [];
    try {
      permissions = JSON.parse(user.permissions || '[]');
    } catch (e) {
      permissions = [];
    }

    res.json({
      token: accessToken,
      refreshToken,
      csrfToken,
      user: {
        id: user.id,
        email: user.email,
        initials: user.initials,
        displayName: user.display_name,
        color: user.color,
        role: user.role || 'user',
        permissions: permissions
      }
    });
  } catch (error) {
    logger.error('Login-Fehler:', { error: error.message });
    res.status(500).json({ error: 'Interner Serverfehler' });
  }
});

/**
 * POST /api/auth/logout
 * Benutzer abmelden und Refresh-Tokens widerrufen
 */
router.post('/logout', authenticateToken, (req, res) => {
  try {
    // Alle Refresh-Tokens des Benutzers löschen
    revokeAllRefreshTokens(req.user.id);
    
    logger.info(`Logout: ${req.user.username}`);
    res.json({ message: 'Erfolgreich abgemeldet' });
  } catch (error) {
    logger.error('Logout-Fehler:', { error: error.message });
    res.status(500).json({ error: 'Logout fehlgeschlagen' });
  }
});

/**
 * GET /api/auth/me
 * Aktuellen Benutzer abrufen
 */
router.get('/me', authenticateToken, (req, res) => {
  try {
    const db = getDb();
    const user = db.prepare('SELECT id, username, display_name, color, role, permissions FROM users WHERE id = ?')
      .get(req.user.id);

    if (!user) {
      return res.status(404).json({ error: 'Benutzer nicht gefunden' });
    }

    // CSRF-Token erneuern
    const csrfToken = getTokenForUser(user.id);

    // Permissions parsen
    let permissions = [];
    try {
      permissions = JSON.parse(user.permissions || '[]');
    } catch (e) {
      permissions = [];
    }

    res.json({
      user: {
        id: user.id,
        username: user.username,
        displayName: user.display_name,
        color: user.color,
        role: user.role || 'user',
        permissions: permissions
      },
      csrfToken
    });
  } catch (error) {
    logger.error('Fehler bei /me:', { error: error.message });
    res.status(500).json({ error: 'Interner Serverfehler' });
  }
});

/**
 * POST /api/auth/refresh
 * Token mit Refresh-Token erneuern
 */
router.post('/refresh', async (req, res) => {
  try {
    const { refreshToken } = req.body;
    const ip = req.ip || req.connection.remoteAddress;
    const userAgent = req.headers['user-agent'];
    
    if (!refreshToken) {
      // Fallback für alte Clients - mit Access Token authentifizieren
      if (req.headers.authorization) {
        return legacyRefresh(req, res);
      }
      return res.status(400).json({ error: 'Refresh-Token erforderlich' });
    }

    // Neuen Access-Token mit Refresh-Token generieren
    const accessToken = await refreshAccessToken(refreshToken, ip, userAgent);
    const db = getDb();
    
    // User-Daten für CSRF-Token abrufen
    const decoded = require('jsonwebtoken').decode(accessToken);
    const csrfToken = getTokenForUser(decoded.id);

    res.json({ 
      token: accessToken,
      csrfToken 
    });
  } catch (error) {
    logger.error('Token-Refresh Fehler:', { error: error.message });
    res.status(401).json({ error: 'Token-Erneuerung fehlgeschlagen' });
  }
});

// Legacy Refresh für Rückwärtskompatibilität
function legacyRefresh(req, res) {
  // Prüfe Authorization Header
  const authHeader = req.headers['authorization'];
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Nicht authentifiziert' });
  }
  
  const token = authHeader.substring(7);
  const user = require('../middleware/auth').verifyToken(token);
  
  if (!user) {
    return res.status(401).json({ error: 'Token ungültig' });
  }
  
  const db = getDb();
  const dbUser = db.prepare('SELECT * FROM users WHERE id = ?').get(user.id);
  
  if (!dbUser) {
    return res.status(404).json({ error: 'Benutzer nicht gefunden' });
  }
  
  const newToken = generateAccessToken(dbUser);
  const csrfToken = getTokenForUser(dbUser.id);
  
  res.json({ token: newToken, csrfToken });
}

/**
 * PUT /api/auth/password
 * Passwort ändern
 */
router.put('/password', authenticateToken, async (req, res) => {
  try {
    const { currentPassword, newPassword } = req.body;

    if (!currentPassword || !newPassword) {
      return res.status(400).json({ error: 'Aktuelles und neues Passwort erforderlich' });
    }

    // Passwort-Richtlinien prüfen
    const passwordErrors = validatePassword(newPassword);
    if (passwordErrors.length > 0) {
      return res.status(400).json({ error: passwordErrors.join('. ') });
    }

    const db = getDb();
    const user = db.prepare('SELECT * FROM users WHERE id = ?').get(req.user.id);

    // Aktuelles Passwort prüfen
    const validPassword = await bcrypt.compare(currentPassword, user.password_hash);
    if (!validPassword) {
      return res.status(401).json({ error: 'Aktuelles Passwort ist falsch' });
    }

    // Neues Passwort hashen und speichern
    const newHash = await bcrypt.hash(newPassword, 12);
    db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(newHash, user.id);

    logger.info(`Passwort geändert: ${user.username}`);
    res.json({ message: 'Passwort erfolgreich geändert' });
  } catch (error) {
    logger.error('Passwort-Änderung Fehler:', { error: error.message });
    res.status(500).json({ error: 'Interner Serverfehler' });
  }
});

/**
 * PUT /api/auth/color
 * Benutzerfarbe ändern
 */
router.put('/color', authenticateToken, (req, res) => {
  try {
    const { color } = req.body;

    if (!color) {
      return res.status(400).json({ error: 'Farbe erforderlich' });
    }

    // Validate hex color format
    const hexColorRegex = /^#[0-9A-Fa-f]{6}$/;
    if (!hexColorRegex.test(color)) {
      return res.status(400).json({ error: 'Ungültiges Farbformat (erwartet: #RRGGBB)' });
    }

    const db = getDb();
    db.prepare('UPDATE users SET color = ? WHERE id = ?').run(color, req.user.id);

    logger.info(`Farbe geändert: ${req.user.username} -> ${color}`);
    res.json({ message: 'Farbe erfolgreich geändert', color });
  } catch (error) {
    logger.error('Fehler beim Ändern der Farbe:', { error: error.message });
    res.status(500).json({ error: 'Interner Serverfehler' });
  }
});

/**
 * GET /api/auth/users
 * Alle Benutzer abrufen (für Zuweisung)
 * Admin-Benutzer werden ausgeschlossen, da sie nur fuer die Benutzerverwaltung sind
 */
router.get('/users', authenticateToken, (req, res) => {
  try {
    const db = getDb();
    // Nur regulaere Benutzer (nicht Admins) fuer Aufgaben-Zuweisung
    const users = db.prepare(`
      SELECT id, email, initials, display_name, color
      FROM users
      WHERE role != 'admin' OR role IS NULL
    `).all();

    res.json(users.map(u => ({
      id: u.id,
      email: u.email,
      initials: u.initials,
      displayName: u.display_name,
      color: u.color
    })));
  } catch (error) {
    logger.error('Fehler beim Abrufen der Benutzer:', { error: error.message });
    res.status(500).json({ error: 'Interner Serverfehler' });
  }
});

module.exports = router;
