const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const nodemailer = require('nodemailer');
const database = require('../config/database');

/**
 * Authentication Service
 * Verwaltet Login, Registrierung, Passwort-Reset
 */
class AuthService {
  constructor() {
    this.jwtSecret = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
    this.jwtExpiration = process.env.JWT_EXPIRATION || '24h';
    this.saltRounds = 10;
    this.emailTransporter = null;
    this.initializeEmailTransporter();
  }

  /**
   * Initialisiert den Email-Transporter
   */
  initializeEmailTransporter() {
    if (process.env.EMAIL_HOST && process.env.EMAIL_USER && process.env.EMAIL_PASSWORD) {
      const port = parseInt(process.env.EMAIL_PORT) || 587;
      const secure = process.env.EMAIL_SECURE === 'true';
      
      // Port 587 = STARTTLS (secure: false)
      // Port 465 = Direct SSL (secure: true)
      const config = {
        host: process.env.EMAIL_HOST,
        port: port,
        secure: secure, // true für Port 465, false für Port 587
        auth: {
          user: process.env.EMAIL_USER,
          pass: process.env.EMAIL_PASSWORD
        }
      };
      
      // Für Port 587: STARTTLS aktivieren
      if (port === 587 && !secure) {
        config.requireTLS = process.env.EMAIL_REQUIRE_TLS !== 'false';
        config.tls = {
          rejectUnauthorized: process.env.NODE_ENV === 'production'
        };
      }
      
      this.emailTransporter = nodemailer.createTransport(config);
      
      console.log('✅ Email-Transporter konfiguriert');
      console.log('   SMTP:', process.env.EMAIL_HOST + ':' + port);
      console.log('   Secure:', secure);
      console.log('   STARTTLS:', port === 587 && !secure ? 'aktiviert' : 'deaktiviert');
    } else {
      console.warn('⚠️  Email-Konfiguration fehlt - Passwort-Reset-E-Mails können nicht versendet werden');
    }
  }

  /**
   * Benutzer registrieren
   * @param {Object} userData - Registrierungsdaten
   * @returns {Promise<Object>} Erstellter Benutzer mit Auth-Info
   */
  async register(userData) {
    const { User, AuthInfo } = database.getModels();
    const { email, password, full_name } = userData;

    // Validierung
    if (!email || !password || !full_name) {
      throw new Error('E-Mail, Passwort und Name sind erforderlich');
    }

    if (password.length < 6) {
      throw new Error('Passwort muss mindestens 6 Zeichen lang sein');
    }

    // Prüfen ob E-Mail bereits existiert
    const existingAuth = await AuthInfo.findOne({ where: { email } });
    if (existingAuth) {
      throw new Error('Diese E-Mail-Adresse ist bereits registriert');
    }

    // Passwort hashen
    const salt = await bcrypt.genSalt(this.saltRounds);
    const passwordHash = await bcrypt.hash(password, salt);

    // Benutzer erstellen
    const user = await User.create({
      full_name,
      role: 0,
      daily_hours: 8,
      week_hours: 40,
      week_workdays: 5,
      preferred_title_type: 0,
      version: 0,
      last_change: new Date()
    });

    // Auth-Info erstellen
    const authInfo = await AuthInfo.create({
      user_id: user.id,
      email,
      password_hash: passwordHash,
      password_method: 'bcrypt',
      password_salt: salt,
      status: 1, // Active
      failed_login_attempts: 0,
      email_token: '',
      email_token_role: 0,
      unverified_email: '',
      version: 0
    });

    return {
      user: {
        id: user.id,
        full_name: user.full_name,
        email: authInfo.email
      }
    };
  }

  /**
   * Benutzer einloggen
   * @param {string} email - E-Mail-Adresse
   * @param {string} password - Passwort
   * @param {string} action - Gewünschte Aktion nach Login (optional: '0', '1', '2', '3', '4')
   * @returns {Promise<Object>} Token und Benutzer-Info
   */
  async login(email, password, action = '0') {
    const { User, AuthInfo, AuthToken } = database.getModels();

    console.log('Login-Versuch für E-Mail:', email);

    // Auth-Info mit Benutzer laden
    const authInfo = await AuthInfo.findOne({
      where: { email },
      include: [{
        model: User,
        as: 'user',
        required: true
      }]
    });

    console.log('AuthInfo gefunden:', !!authInfo);
    if (authInfo) {
      console.log('AuthInfo E-Mail:', authInfo.email);
      console.log('User ID:', authInfo.user?.id);
    }

    if (!authInfo) {
      throw new Error('Ungültige E-Mail oder Passwort');
    }

    // Account-Status prüfen
    if (authInfo.status !== 1) {
      throw new Error('Ihr Account ist deaktiviert. Bitte kontaktieren Sie den Support.');
    }

    // Zu viele fehlgeschlagene Versuche?
    if (authInfo.failed_login_attempts >= 5) {
      const lastAttempt = authInfo.last_login_attempt;
      const lockoutTime = 15 * 60 * 1000; // 15 Minuten
      
      if (lastAttempt && (Date.now() - new Date(lastAttempt).getTime()) < lockoutTime) {
        throw new Error('Account temporär gesperrt. Bitte versuchen Sie es später erneut.');
      } else {
        // Reset nach Lockout-Zeit
        await authInfo.update({
          failed_login_attempts: 0
        });
      }
    }

    // Passwort verifizieren
    const isValidPassword = await bcrypt.compare(password, authInfo.password_hash);

    if (!isValidPassword) {
      // Fehlversuch registrieren
      await authInfo.update({
        failed_login_attempts: authInfo.failed_login_attempts + 1,
        last_login_attempt: new Date()
      });
      throw new Error('Ungültige E-Mail oder Passwort');
    }

    // Login erfolgreich - Reset fehlgeschlagene Versuche
    await authInfo.update({
      failed_login_attempts: 0,
      last_login_attempt: new Date()
    });

    // JWT Token generieren
    const token = jwt.sign(
      {
        userId: authInfo.user.id,
        email: authInfo.email,
        role: authInfo.user.role
      },
      this.jwtSecret,
      { expiresIn: this.jwtExpiration }
    );

    // Token in Datenbank speichern
    const expiresAt = new Date();
    expiresAt.setHours(expiresAt.getHours() + 24);

    await AuthToken.create({
      auth_info_id: authInfo.id,
      value: crypto.createHash('sha256').update(token).digest('hex'),
      expires: expiresAt,
      version: 0
    });

    const result = {
      token,
      user: {
        id: authInfo.user.id,
        full_name: authInfo.user.full_name,
        email: authInfo.email,
        role: authInfo.user.role
      }
    };

    // Führe gewünschte Aktion aus (falls angegeben und gültig)
    if (action && action !== '0') {
      console.log(`🔔 LOGIN MIT AKTION: User ${authInfo.user.full_name} (ID ${authInfo.user.id}), Aktion: ${action}`);
      try {
        const actionWarning = await this._executeLoginAction(authInfo.user.id, action);
        if (actionWarning) {
          result.actionWarning = actionWarning;
          console.log(`⚠️  Login-Aktion konnte nicht ausgeführt werden: ${actionWarning}`);
        } else {
          console.log(`✅ Login-Aktion erfolgreich ausgeführt`);
        }
      } catch (error) {
        console.error('❌ Fehler beim Ausführen der Login-Aktion:', error);
        result.actionWarning = `Aktion konnte nicht ausgeführt werden: ${error.message}`;
      }
    }

    return result;
  }

  /**
   * Führt die gewünschte Aktion nach dem Login aus
   * @param {number} userId - Benutzer-ID
   * @param {string} action - Gewünschte Aktion ('1', '2', '3', '4')
   * @returns {Promise<string|null>} Warnung, falls Aktion nicht ausgeführt werden konnte
   * @private
   */
  async _executeLoginAction(userId, action) {
    const timeEntryService = require('./TimeEntryService');
    
    // Hole den aktuellen Status
    const currentState = await timeEntryService.getCurrentState(userId);
    
    console.log(`Login-Aktion angefordert: ${action}, aktueller Status: ${currentState || 'null'}`);
    
    // Mapping: action-Nummer -> Worklog-Action
    const actionMap = {
      '1': 'start work',      // Arbeit beginnen
      '2': 'start pause',     // Pause beginnen
      '3': 'stop pause',      // Pause beenden
      '4': 'stop work'        // Feierabend
    };
    
    const worklogAction = actionMap[action];
    if (!worklogAction) {
      return 'Ungültige Aktion';
    }
    
    // Validiere, ob die Aktion erlaubt ist
    const isValid = this._isValidLoginAction(currentState, worklogAction);
    
    if (!isValid) {
      const actionNames = {
        'start work': 'Arbeit beginnen',
        'start pause': 'Pause beginnen',
        'stop pause': 'Pause beenden',
        'stop work': 'Feierabend'
      };
      
      const currentStateNames = {
        'start work': 'Arbeit läuft',
        'stop work': 'Feierabend',
        'start pause': 'Pause läuft',
        'stop pause': 'Pause beendet',
        'null': 'Keine Aktion'
      };
      
      const actionName = actionNames[worklogAction] || worklogAction;
      const currentStateName = currentStateNames[currentState || 'null'] || currentState;
      
      return `Aktion "${actionName}" kann nicht ausgeführt werden (aktueller Status: ${currentStateName})`;
    }
    
    // Führe die Aktion aus
    try {
      await timeEntryService.clock(userId, worklogAction);
      console.log(`Login-Aktion erfolgreich ausgeführt: ${worklogAction}`);
      return null; // Kein Fehler
    } catch (error) {
      console.error('Fehler beim Ausführen der Login-Aktion:', error);
      return `Aktion konnte nicht ausgeführt werden: ${error.message}`;
    }
  }

  /**
   * Validiert, ob eine Login-Aktion erlaubt ist
   * @param {string|null} currentState - Aktueller Status
   * @param {string} action - Gewünschte Aktion
   * @returns {boolean} True, wenn die Aktion erlaubt ist
   * @private
   */
  _isValidLoginAction(currentState, action) {
    // Erlaubte Übergänge basierend auf den Anforderungen:
    // 1. Arbeit beginnen: user hat noch gar keine Aktion, oder letzte Aktion war stop work
    // 2. Pause beginnen: letzte Aktion "start work" oder "stop pause"
    // 3. Pause beenden: letzte Aktion "start pause"
    // 4. Feierabend: letzte Aktion "start work" oder "stop pause"
    
    const allowedTransitions = {
      'start work': ['null', 'stop work'],
      'start pause': ['start work', 'stop pause'],
      'stop pause': ['start pause'],
      'stop work': ['start work', 'stop pause']
    };
    
    const allowed = allowedTransitions[action] || [];
    const stateToCheck = currentState || 'null';
    
    return allowed.includes(stateToCheck);
  }

  /**
   * Token validieren
   * @param {string} token - JWT Token
   * @returns {Promise<Object>} Dekodierte Token-Daten
   */
  async validateToken(token) {
    try {
      const decoded = jwt.verify(token, this.jwtSecret);
      
      // Prüfen ob Token in DB existiert und nicht abgelaufen
      const { AuthToken } = database.getModels();
      const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
      
      const dbToken = await AuthToken.findOne({
        where: { value: tokenHash }
      });

      if (!dbToken || dbToken.isExpired()) {
        throw new Error('Token ungültig oder abgelaufen');
      }

      return decoded;
    } catch (error) {
      throw new Error('Ungültiger Token');
    }
  }

  /**
   * Passwort-Reset anfordern
   * @param {string} email - E-Mail-Adresse
   * @returns {Promise<string>} Reset-Token
   */
  async requestPasswordReset(email) {
    const { AuthInfo } = database.getModels();

    console.log('🔐 Passwort-Reset angefordert für E-Mail:', email);

    const authInfo = await AuthInfo.findOne({ where: { email } });
    
    if (!authInfo) {
      // Aus Sicherheitsgründen keine Fehlermeldung, dass E-Mail nicht existiert
      console.log('⚠️  Kein Benutzer mit E-Mail gefunden:', email);
      console.log('   (Aus Sicherheitsgründen wird trotzdem 200 OK zurückgegeben)');
      return null;
    }

    console.log('✅ Benutzer gefunden - User ID:', authInfo.user_id);

    // Reset-Token generieren
    const resetToken = crypto.randomBytes(32).toString('hex');
    const tokenExpires = new Date();
    tokenExpires.setHours(tokenExpires.getHours() + 1); // 1 Stunde gültig

    console.log('🔑 Reset-Token generiert, gültig bis:', tokenExpires.toLocaleString('de-DE'));

    // Token speichern
    await authInfo.update({
      email_token: resetToken,
      email_token_expires: tokenExpires,
      email_token_role: 1 // 1 = Password Reset
    });

    console.log('💾 Reset-Token in Datenbank gespeichert');

    // E-Mail versenden (falls konfiguriert)
    if (this.emailTransporter) {
      console.log('📧 E-Mail-Transporter verfügbar - bereite E-Mail vor...');
      
      const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5010';
      const resetUrl = `${frontendUrl}/password-reset?token=${resetToken}`;

      console.log('🔗 Reset-URL:', resetUrl);

      const mailOptions = {
        from: `"${process.env.EMAIL_FROM_NAME || 'TimeClock'}" <${process.env.EMAIL_FROM}>`,
        to: email,
        subject: 'TimeClock - Passwort zurücksetzen',
        text: `Hallo,

du hast eine Passwort-Zurücksetzen-Anfrage für dein TimeClock-Konto gestellt.

Klicke auf folgenden Link um dein Passwort zurückzusetzen:
${resetUrl}

Dieser Link ist 1 Stunde gültig.

Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail.

---
TimeClock Zeiterfassung
${frontendUrl}
`,
        html: `
          <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
            <h2 style="color: #333;">Passwort zurücksetzen</h2>
            <p>Hallo,</p>
            <p>du hast eine Passwort-Zurücksetzen-Anfrage für dein TimeClock-Konto gestellt.</p>
            <p>
              <a href="${resetUrl}" style="display: inline-block; padding: 12px 24px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 16px 0;">
                Passwort zurücksetzen
              </a>
            </p>
            <p style="color: #666; font-size: 14px;">
              Oder kopiere diesen Link in deinen Browser:<br>
              <code style="background: #f5f5f5; padding: 4px 8px; border-radius: 4px; display: inline-block; margin-top: 8px;">${resetUrl}</code>
            </p>
            <p style="color: #666; font-size: 14px;">
              <strong>Dieser Link ist 1 Stunde gültig.</strong>
            </p>
            <p style="color: #666; font-size: 14px;">
              Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail.
            </p>
            <hr style="border: none; border-top: 1px solid #ddd; margin: 24px 0;">
            <p style="color: #999; font-size: 12px;">
              TimeClock Zeiterfassung<br>
              <a href="${frontendUrl}">${frontendUrl}</a>
            </p>
          </div>
        `
      };

      try {
        console.log('📤 Sende E-Mail an:', email);
        console.log('   Von:', mailOptions.from);
        console.log('   SMTP-Server:', process.env.EMAIL_HOST + ':' + process.env.EMAIL_PORT);
        
        const info = await this.emailTransporter.sendMail(mailOptions);
        
        console.log('✅ Passwort-Reset E-Mail erfolgreich gesendet!');
        console.log('   Empfänger:', email);
        console.log('   Message-ID:', info.messageId);
        console.log('   Response:', info.response);
      } catch (error) {
        console.error('❌ Fehler beim E-Mail-Versand!');
        console.error('   Error:', error.message);
        console.error('   Code:', error.code);
        if (error.code === 'EAUTH') {
          console.error('   💡 SMTP-Authentifizierung fehlgeschlagen - prüfe EMAIL_USER/PASSWORD in .env');
        } else if (error.code === 'ECONNECTION' || error.code === 'ETIMEDOUT') {
          console.error('   💡 Verbindung zum SMTP-Server fehlgeschlagen - prüfe EMAIL_HOST/PORT/Firewall');
        }
        // Werfe keinen Fehler - Token wurde gespeichert, User kann es anders nutzen
      }
    } else {
      console.warn('⚠️  E-Mail-Transporter NICHT konfiguriert!');
      console.warn('   Reset-Token wurde erstellt, aber KEINE E-Mail versendet');
      console.warn('   Prüfe EMAIL_HOST, EMAIL_USER, EMAIL_PASSWORD in .env');
      console.log('📋 Reset-Token (manuell verwenden):', resetToken);
      console.log('   URL: ' + (process.env.FRONTEND_URL || 'http://localhost:5010') + '/password-reset?token=' + resetToken);
    }

    return resetToken;
  }

  /**
   * Passwort zurücksetzen
   * @param {string} token - Reset-Token
   * @param {string} newPassword - Neues Passwort
   * @returns {Promise<boolean>} Erfolg
   */
  async resetPassword(token, newPassword) {
    const { AuthInfo } = database.getModels();

    if (newPassword.length < 6) {
      throw new Error('Passwort muss mindestens 6 Zeichen lang sein');
    }

    // Development-Token erkennen und behandeln
    const isDevelopment = !process.env.NODE_ENV || process.env.NODE_ENV === 'development';
    const isDevToken = token && token.startsWith('dev-test-token-');

    if (isDevelopment && isDevToken) {
      // Im Development-Modus: Token akzeptieren und Passwort für spezifischen Benutzer ändern
      console.log('Development-Token erkannt:', token);
      
      // E-Mail aus Token extrahieren (Format: dev-test-token-timestamp-email)
      // Token: "dev-test-token-1760554590751-tsschulz@tsschulz.de"
      const emailMatch = token.match(/dev-test-token-\d+-(.+)/);
      const email = emailMatch ? emailMatch[1] : null;
      
      console.log('Development: Extrahierte E-Mail:', email);
      
      if (email) {
        // Finde den Benutzer mit dieser E-Mail
        const authInfo = await AuthInfo.findOne({
          where: { email: email }
        });
        
        if (authInfo) {
          console.log('Development: Ändere Passwort für Benutzer:', authInfo.email);
          
          // Neues Passwort hashen und speichern
          const salt = await bcrypt.genSalt(this.saltRounds);
          const passwordHash = await bcrypt.hash(newPassword, salt);
          
          await authInfo.update({
            password_hash: passwordHash,
            password_salt: salt,
            email_token: '', // Token löschen
            email_token_expires: null,
            email_token_role: 0
          });
          
          console.log('Development: Passwort erfolgreich geändert für:', authInfo.email);
          return true;
        } else {
          console.log('Development: Kein Benutzer mit E-Mail', email, 'gefunden');
        }
      }
      
      // Fallback: Ändere Passwort für ersten Benutzer
      const authInfo = await AuthInfo.findOne({
        order: [['id', 'ASC']]
      });
      
      if (authInfo) {
        console.log('Development: Fallback - Ändere Passwort für ersten Benutzer:', authInfo.email);
        
        const salt = await bcrypt.genSalt(this.saltRounds);
        const passwordHash = await bcrypt.hash(newPassword, salt);
        
        await authInfo.update({
          password_hash: passwordHash,
          password_salt: salt,
          email_token: '',
          email_token_expires: null,
          email_token_role: 0
        });
        
        console.log('Development: Passwort erfolgreich geändert für:', authInfo.email);
        return true;
      } else {
        console.log('Development: Kein Benutzer in der Datenbank gefunden');
        return true;
      }
    }

    const authInfo = await AuthInfo.findOne({
      where: {
        email_token: token,
        email_token_role: 1
      }
    });

    if (!authInfo) {
      throw new Error('Ungültiger oder abgelaufener Reset-Token');
    }

    // Token-Ablauf prüfen
    if (authInfo.email_token_expires && new Date() > new Date(authInfo.email_token_expires)) {
      throw new Error('Reset-Token ist abgelaufen');
    }

    // Neues Passwort hashen
    const salt = await bcrypt.genSalt(this.saltRounds);
    const passwordHash = await bcrypt.hash(newPassword, salt);

    // Passwort aktualisieren und Token löschen
    await authInfo.update({
      password_hash: passwordHash,
      password_salt: salt,
      email_token: '',
      email_token_expires: null,
      email_token_role: 0,
      failed_login_attempts: 0
    });

    return true;
  }

  /**
   * Passwort ändern (für eingeloggten Benutzer)
   * @param {number} userId - Benutzer-ID
   * @param {string} oldPassword - Altes Passwort
   * @param {string} newPassword - Neues Passwort
   * @returns {Promise<boolean>} Erfolg
   */
  async changePassword(userId, oldPassword, newPassword) {
    const { User, AuthInfo } = database.getModels();

    if (newPassword.length < 6) {
      throw new Error('Passwort muss mindestens 6 Zeichen lang sein');
    }

    const user = await User.findByPk(userId, {
      include: [{
        model: AuthInfo,
        as: 'authInfo',
        required: true
      }]
    });

    if (!user || !user.authInfo) {
      throw new Error('Benutzer nicht gefunden');
    }

    // Altes Passwort verifizieren
    const isValidPassword = await bcrypt.compare(oldPassword, user.authInfo.password_hash);
    
    if (!isValidPassword) {
      throw new Error('Altes Passwort ist falsch');
    }

    // Neues Passwort hashen
    const salt = await bcrypt.genSalt(this.saltRounds);
    const passwordHash = await bcrypt.hash(newPassword, salt);

    // Passwort aktualisieren
    await user.authInfo.update({
      password_hash: passwordHash,
      password_salt: salt,
      version: user.authInfo.version + 1
    });

    return true;
  }

  /**
   * Ausloggen (Token invalidieren)
   * @param {string} token - JWT Token
   * @returns {Promise<boolean>} Erfolg
   */
  async logout(token) {
    const { AuthToken } = database.getModels();
    
    const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
    
    await AuthToken.destroy({
      where: { value: tokenHash }
    });

    return true;
  }

  /**
   * Benutzer-Profil abrufen
   * @param {number} userId - Benutzer-ID
   * @returns {Promise<Object>} Benutzer-Profil
   */
  async getUserProfile(userId) {
    const { User, AuthInfo } = database.getModels();

    const user = await User.findByPk(userId, {
      include: [{
        model: AuthInfo,
        as: 'authInfo',
        attributes: ['email']
      }]
    });

    if (!user) {
      throw new Error('Benutzer nicht gefunden');
    }

    return {
      id: user.id,
      full_name: user.full_name,
      email: user.authInfo?.email,
      role: user.role,
      daily_hours: user.daily_hours,
      week_hours: user.week_hours,
      week_workdays: user.week_workdays
    };
  }
}

module.exports = new AuthService();

