const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { User } = require('../models');
const AppError = require('../utils/appError');
const logger = require('../utils/logger');
const { sequelize } = require('../models');
const redis = require('../utils/redis');

class AuthService {
  static async signup(data) {
    const transaction = await sequelize.transaction();
    try {
      const existingUser = await User.findOne({ 
        where: { email: data.email },
        transaction
      });

      if (existingUser) {
        throw new AppError('Email already exists', 400);
      }

      const hashedPassword = await bcrypt.hash(data.password, 12);
      const user = await User.create({
        ...data,
        password: hashedPassword
      }, { transaction });

      const token = this.generateToken(user.id);
      await transaction.commit();

      logger.info(`New user registered: ${user.email}`);
      await this.cacheUserData(user.id, this.sanitizeUser(user));

      return { 
        token, 
        user: this.sanitizeUser(user)
      };
    } catch (error) {
      await transaction.rollback();
      logger.error('Signup error:', error);
      throw error;
    }
  }

  static async login(data) {
    try {
      const user = await User.findOne({ 
        where: { 
          email: data.email,
          status: 'active'
        }
      });

      if (!user || !(await this.verifyPassword(data.password, user.password))) {
        throw new AppError('Invalid credentials', 401);
      }

      const loginAttempts = await redis.get(`loginAttempts:${data.email}`);
      if (loginAttempts >= process.env.MAX_LOGIN_ATTEMPTS) {
        throw new AppError('Account temporarily locked', 423);
      }

      const token = this.generateToken(user.id);
      await redis.del(`loginAttempts:${data.email}`);
      logger.info(`User logged in: ${user.email}`);

      return { 
        token, 
        user: this.sanitizeUser(user)
      };
    } catch (error) {
      logger.error('Login error:', error);
      throw error;
    }
  }

  static async cacheUserData(userId, userData) {
    await redis.setex(`user:${userId}`, 3600, JSON.stringify(userData));
  }

  static generateToken(userId) {
    return jwt.sign(
      { id: userId }, 
      process.env.JWT_SECRET, 
      { expiresIn: process.env.JWT_EXPIRES_IN }
    );
  }

  static async verifyPassword(candidatePassword, hashedPassword) {
    return await bcrypt.compare(candidatePassword, hashedPassword);
  }

  static sanitizeUser(user) {
    return {
      id: user.id,
      email: user.email,
      role: user.role,
      firstName: user.firstName,
      lastName: user.lastName,
      status: user.status
    };
  }
}

module.exports = AuthService;