const bcrypt = require('bcryptjs');
const { User, PasswordResetToken } = require('../models');
const jwt = require('jsonwebtoken');
const { addTokenToBlacklist } = require('../utils/blacklist');
const { transporter, getPasswordResetEmailTemplate } = require('../config/email');
const crypto = require('crypto');

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

exports.register = async (req, res) => {
  const { name, email, password } = req.body;
  if (!name || !email || !password) {
    return res.status(400).json({ message: 'Alle Felder sind erforderlich' });
  }
  try {
    const hashedPassword = await bcrypt.hash(password, 10);
    console.log('Register: creating user', { email });

    const maxAttempts = 3;
    let attempt = 0;
    let createdUser = null;
    let lastError = null;

    while (attempt < maxAttempts && !createdUser) {
      try {
        createdUser = await User.create({ name, email, password: hashedPassword, active: false });
      } catch (err) {
        lastError = err;
        // Spezifisch auf Lock-Timeout reagieren und erneut versuchen
        if ((err.code === 'ER_LOCK_WAIT_TIMEOUT' || err?.parent?.code === 'ER_LOCK_WAIT_TIMEOUT') && attempt < maxAttempts - 1) {
          const backoffMs = 300 * (attempt + 1);
          console.warn(`Register: ER_LOCK_WAIT_TIMEOUT, retry in ${backoffMs}ms (attempt ${attempt + 1}/${maxAttempts})`);
          await delay(backoffMs);
          attempt++;
          continue;
        }
        throw err;
      }
    }

    if (!createdUser && lastError) {
      console.error('Register error (after retries):', lastError);
      return res.status(503).json({ message: 'Zeitüberschreitung beim Zugriff auf die Datenbank. Bitte erneut versuchen.' });
    }

    console.log('Register: user created', { id: createdUser.id });

    const safeUser = {
      id: createdUser.id,
      name: createdUser.name,
      email: createdUser.email,
      active: createdUser.active,
      created_at: createdUser.created_at
    };

    return res.status(201).json({ message: 'Benutzer erfolgreich registriert', user: safeUser });
  } catch (error) {
    if (error.name === 'SequelizeUniqueConstraintError') {
      return res.status(400).json({ message: 'Email-Adresse bereits in Verwendung' });
    }
    console.error('Register error:', error);
    return res.status(500).json({ message: 'Ein Fehler ist aufgetreten', error: error.message });
  }
};

exports.login = async (req, res) => {
  const { email, password } = req.body;
  if (!email || !password) {
    return res.status(400).json({ message: 'Email und Passwort sind erforderlich' });
  }
  try {
    const user = await User.findOne({ where: { email } });
    if (!user) {
      return res.status(401).json({ message: 'Ungültige Anmeldedaten' });
    }
    const validPassword = await bcrypt.compare(password, user.password);
    if (!validPassword) {
      return res.status(401).json({ message: 'Ungültige Anmeldedaten' });
    }
    if (!user.active) {
      return res.status(403).json({ message: 'Benutzerkonto ist nicht aktiv' });
    }
    const token = jwt.sign({ id: user.id, name: user.name, email: user.email }, 'zTxVgptmPl9!_dr%xxx9999(dd)', { expiresIn: '1h' });
    return res.status(200).json({ message: 'Login erfolgreich', token, 'user': user });
  } catch (error) {
    return res.status(500).json({ message: 'Ein Fehler ist aufgetreten' });
  }
};

exports.forgotPassword = async (req, res) => {
  const { email } = req.body;
  if (!email) {
    return res.status(400).json({ message: 'E-Mail-Adresse ist erforderlich' });
  }
  try {
    const user = await User.findOne({ where: { email } });
    if (!user) {
      // Aus Sicherheitsgründen immer Erfolg melden, auch wenn E-Mail nicht existiert
      return res.status(200).json({ message: 'Falls die E-Mail-Adresse in unserem System registriert ist, erhalten Sie einen Link zum Zurücksetzen des Passworts.' });
    }

    // Alte Reset-Tokens für diesen User löschen
    await PasswordResetToken.destroy({ where: { userId: user.id } });

    // Neuen Reset-Token generieren
    const token = crypto.randomBytes(32).toString('hex');
    const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 Stunde

    await PasswordResetToken.create({
      userId: user.id,
      token,
      expiresAt
    });

    // Reset-URL generieren
    const resetUrl = `${process.env.FRONTEND_URL || 'http://localhost:8080'}/reset-password?token=${token}`;

    // E-Mail versenden
    const emailTemplate = getPasswordResetEmailTemplate(resetUrl, user.name);
    
    const mailOptions = {
      from: process.env.SMTP_FROM || 'noreply@miriamgemeinde.de',
      to: email,
      subject: emailTemplate.subject,
      html: emailTemplate.html,
      text: emailTemplate.text
    };
    
    console.log('=== EMAIL SENDING DEBUG ===');
    console.log('From:', mailOptions.from);
    console.log('To:', mailOptions.to);
    console.log('Subject:', mailOptions.subject);
    console.log('Reset URL:', resetUrl);
    console.log('===========================');
    
    await transporter.sendMail(mailOptions);

    console.log('Password reset email sent to:', email);
    return res.status(200).json({ message: 'Falls die E-Mail-Adresse in unserem System registriert ist, erhalten Sie einen Link zum Zurücksetzen des Passworts.' });
  } catch (error) {
    console.error('Forgot password error:', error);
    return res.status(500).json({ message: 'Ein Fehler ist aufgetreten' });
  }
};

exports.resetPassword = async (req, res) => {
  const { token, password } = req.body;
  if (!token || !password) {
    return res.status(400).json({ message: 'Token und neues Passwort sind erforderlich' });
  }
  if (password.length < 6) {
    return res.status(400).json({ message: 'Passwort muss mindestens 6 Zeichen lang sein' });
  }

  try {
    // Token validieren
    const resetToken = await PasswordResetToken.findOne({
      where: {
        token,
        used: false,
        expiresAt: {
          [require('sequelize').Op.gt]: new Date()
        }
      },
      include: [{ model: User, as: 'user' }]
    });

    if (!resetToken) {
      return res.status(400).json({ message: 'Ungültiger oder abgelaufener Token' });
    }

    // Passwort hashen und aktualisieren
    const hashedPassword = await bcrypt.hash(password, 10);
    await User.update(
      { password: hashedPassword },
      { where: { id: resetToken.userId } }
    );

    // Token als verwendet markieren
    await resetToken.update({ used: true });

    console.log('Password reset successful for user:', resetToken.userId);
    return res.status(200).json({ message: 'Passwort erfolgreich zurückgesetzt' });
  } catch (error) {
    console.error('Reset password error:', error);
    return res.status(500).json({ message: 'Ein Fehler ist aufgetreten' });
  }
};

exports.logout = async (req, res) => {
  const authHeader = req.header('Authorization');
  if (!authHeader) {
    return res.status(400).json({ message: 'Kein Token bereitgestellt' });
  }
  const token = authHeader.replace('Bearer ', '');
  try {
    addTokenToBlacklist(token);
    return res.status(200).json({ message: 'Logout erfolgreich' });
  } catch (error) {
    console.log(error);
    return res.status(500).json({ message: 'Ein Fehler ist beim Logout aufgetreten' });
  }
};
