const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { Admin, Doctor, Cliente } = require('../models');
const logger = require('../utils/logger');

// Mapeo de tipos de usuario a modelos
const userModels = {
  admin: Admin,
  doctor: Doctor,
  cliente: Cliente
};

// Generar token JWT
const generateToken = (user, type) => {
  const payload = {
    id: user.Id,
    email: user.Email,
    type: type
  };

  return jwt.sign(payload, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRES_IN || '24h'
  });
};

/**
 * Login universal para Admin, Doctor y Cliente
 */
const login = async (req, res) => {
  try {
    const { email, password, userType } = req.body;
    if (!userType || !userModels[userType]) {
      logger.error('Tipo de usuario inválido', { userType });
      return res.status(400).json({
        success: false,
        message: 'Tipo de usuario inválido'
      });
    }

    const Model = userModels[userType];
    // Buscar usuario por email
    const user = await Model.findOne({
      where: { Email: email }
    });

    if (!user) {
      logger.warn(`Login fallido - Usuario no encontrado`, { 
        email, 
        userType,
        ip: req.ip 
      });
      return res.status(401).json({
        success: false,
        message: 'Credenciales inválidas - Correo no encontrado'
      });
    }

    // Verificar contraseña
    const isPasswordValid = await bcrypt.compare(password, user.Password);
    // Solo para probar YA
    
    if (!isPasswordValid) {
      logger.warn(`Login fallido - Contraseña incorrecta`, { 
        email, 
        userType,
        ip: req.ip 
      });
      return res.status(401).json({
        success: false,
        message: 'Credenciales inválidas'
      });
    }

    // Generar token
    const token = generateToken(user, userType);

    // Log exitoso
    logger.auth(`Login exitoso - ${userType}`, {
      userId: user.Id,
      email: user.Email,
      userType,
      ip: req.ip
    });

    // Remover password de la respuesta
    const userResponse = user.toJSON();
    delete userResponse.Password;

    res.json({
      success: true,
      message: 'Login exitoso',
      data: {
        user: userResponse,
        token,
        type: userType
      }
    });

  } catch (error) {
    logger.error('Error en login', { 
      error: error.message, 
      stack: error.stack,
      userType: req.userType 
    });
    res.status(500).json({
      success: false,
      message: 'Error en el servidor',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Registro universal para Admin, Doctor y Cliente
 */
const register = async (req, res) => {
  try {
    const userType = req.userType;
    const userData = req.body;
    console.log('Tipo de Usuario: ', userType);
    console.log('Datos de Usuario: ', userData);

    if (!userType || !userModels[userType]) {
      return res.status(400).json({
        success: false,
        message: 'Tipo de usuario inválido'
      });
    }

    const Model = userModels[userType];

    // Verificar si el email ya existe
    const existingUser = await Model.findOne({
      where: { Email: userData.email || userData.Email }
    });

    if (existingUser) {
      logger.warn(`Registro fallido - Email ya existe`, { 
        email: userData.email || userData.Email, 
        userType 
      });
      return res.status(400).json({
        success: false,
        message: 'El email ya está registrado'
      });
    }

    // Hashear contraseña
    const hashedPassword = await bcrypt.hash(
      userData.password || userData.Password, 
      parseInt(process.env.BCRYPT_ROUNDS) || 10
    );

    // Preparar datos para crear usuario
    const newUserData = {
      ...userData,
      Password: hashedPassword
    };

    // Eliminar password sin hashear si existe
    delete newUserData.password;

    // Crear usuario
    const newUser = await Model.create(newUserData);

    // Generar token
    const token = generateToken(newUser, userType);

    // Log exitoso
    logger.auth(`Registro exitoso - ${userType}`, {
      userId: newUser.Id,
      email: newUser.Email,
      userType,
      ip: req.ip
    });

    // Remover password de la respuesta
    const userResponse = newUser.toJSON();
    delete userResponse.Password;

    res.status(201).json({
      success: true,
      message: 'Registro exitoso',
      data: {
        user: userResponse,
        token,
        type: userType
      }
    });

  } catch (error) {
    logger.error('Error en registro', { 
      error: error.message, 
      stack: error.stack,
      userType: req.userType 
    });

    // Manejar errores de validación de Sequelize
    if (error.name === 'SequelizeValidationError') {
      return res.status(400).json({
        success: false,
        message: 'Error de validación',
        errors: error.errors.map(e => e.message)
      });
    }

    res.status(500).json({
      success: false,
      message: 'Error en el servidor',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Cambiar contraseña (requiere autenticación)
 */
const changePasswordsintoken = async (req, res) => {
  try {
    const { newPassword, id, type } = req.body;
    if (!type || !userModels[type]) {
      return res.status(400).json({
        success: false,
        message: 'Tipo de usuario inválido'
      });
    }
    const Model = userModels[type];
    // Buscar usuario
    const user = await Model.findByPk(id);
    if (!user) {
      logger.warn(`Cambio de contraseña fallido - Usuario no encontrado`, { 
        userId: id, 
        userType: type 
      });
      return res.status(404).json({
        success: false,
        message: 'Usuario no encontrado'
      });
    }
    // Hashear nueva contraseña
    const hashedPassword = await bcrypt.hash(
      newPassword, 
      parseInt(process.env.BCRYPT_ROUNDS) || 10
    );
    // Actualizar contraseña
    await user.update(
      {
        Password: hashedPassword,
        Code: 'sin codigo'
      }
    );
    // Log exitoso
    logger.auth(`Contraseña cambiada exitosamente - ${type}`, {
      userId: id,
      userType: type,
      ip: req.ip
    });
    res.json({
      success: true,
      message: 'Contraseña cambiada exitosamente'
    });
  } catch (error) {
    logger.error('Error al cambiar contraseña', { 
      error: error.message, 
      stack: error.stack,
      userId: req.user?.id 
    });
    res.status(500).json({
      success: false,
      message: 'Error en el servidor',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Cambiar contraseña (requiere autenticación)
 */
const changePassword = async (req, res) => {
  try {
    const { oldPassword, newPassword } = req.body;
    const { id, type } = req.user; // Viene del middleware verifyToken
    if (!type || !userModels[type]) {
      return res.status(400).json({
        success: false,
        message: 'Tipo de usuario inválido'
      });
    }
    const Model = userModels[type];
    // Buscar usuario
    const user = await Model.findByPk(id);
    if (!user) {
      logger.warn(`Cambio de contraseña fallido - Usuario no encontrado`, { 
        userId: id, 
        userType: type 
      });
      return res.status(404).json({
        success: false,
        message: 'Usuario no encontrado'
      });
    }
    // Verificar contraseña actual
    const isPasswordValid = await bcrypt.compare(oldPassword, user.Password);
    if (!isPasswordValid) {
      logger.warn(`Cambio de contraseña fallido - Contraseña incorrecta`, { 
        userId: id, 
        userType: type,
        ip: req.ip 
      });
      return res.status(401).json({
        success: false,
        message: 'Contraseña actual incorrecta'
      });
    }
    // Hashear nueva contraseña
    const hashedPassword = await bcrypt.hash(
      newPassword, 
      parseInt(process.env.BCRYPT_ROUNDS) || 10
    );
    // Actualizar contraseña
    await user.update({ Password: hashedPassword });
    // Log exitoso
    logger.auth(`Contraseña cambiada exitosamente - ${type}`, {
      userId: id,
      userType: type,
      ip: req.ip
    });
    res.json({
      success: true,
      message: 'Contraseña cambiada exitosamente'
    });
  } catch (error) {
    logger.error('Error al cambiar contraseña', { 
      error: error.message, 
      stack: error.stack,
      userId: req.user?.id 
    });
    res.status(500).json({
      success: false,
      message: 'Error en el servidor',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Verificar token (útil para validar sesión)
 */
const verifyTokenEndpoint = async (req, res) => {
  try {
    const { id, type } = req.user; // Viene del middleware verifyToken

    if (!type || !userModels[type]) {
      return res.status(400).json({
        success: false,
        message: 'Tipo de usuario inválido'
      });
    }

    const Model = userModels[type];

    // Buscar usuario
    const user = await Model.findByPk(id);

    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Usuario no encontrado'
      });
    }

    // Remover password de la respuesta
    const userResponse = user.toJSON();
    delete userResponse.Password;

    res.json({
      success: true,
      message: 'Token válido',
      data: {
        user: userResponse,
        type: type
      }
    });

  } catch (error) {
    logger.error('Error al verificar token', { 
      error: error.message, 
      userId: req.user?.id 
    });
    res.status(500).json({
      success: false,
      message: 'Error en el servidor'
    });
  }
};

/**
 * Logout (opcional - para invalidar token en el cliente)
 */
const logout = async (req, res) => {
  try {
    const { id, type } = req.user;

    logger.auth(`Logout - ${type}`, {
      userId: id,
      userType: type,
      ip: req.ip
    });

    res.json({
      success: true,
      message: 'Logout exitoso'
    });

  } catch (error) {
    logger.error('Error en logout', { error: error.message });
    res.status(500).json({
      success: false,
      message: 'Error en el servidor'
    });
  }
};

module.exports = {
  login,
  register,
  changePassword,
  changePasswordsintoken,
  verifyTokenEndpoint,
  logout
};