const { User, Wallet, Transaction, ActivityLog, sequelize } = require('../models');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const { Op } = require('sequelize');
const sendEmail = require('../utils/sendEmail');

const generateToken = (id) => {
  return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d' });
};

exports.register = async (req, res) => {
  const { username, email, password, referralCode } = req.body;
  const t = await sequelize.transaction();

  try {
    // Verificar si el usuario ya existe
    const userExists = await User.findOne({ where: { email } });
    if (userExists) {
      await t.rollback();
      return res.status(400).json({ message: 'El usuario ya existe' });
    }

    let parentId = null;
    if (referralCode) {
      const parentUser = await User.findOne({ where: { referralCode } });
      if (parentUser) {
        parentId = parentUser.id;
      }
    }

    const user = await User.create({
      username,
      email,
      password,
      parentId
    }, { transaction: t });

    // Crear Wallet para el usuario
    await Wallet.create({ userId: user.id }, { transaction: t });

    // Lógica de Recompensa por Referido (Punto 5)
    if (parentId) {
      const REFERRAL_BONUS = 1.00; // Monto configurable
      const parentWallet = await Wallet.findOne({ where: { userId: parentId } });
      
      if (parentWallet) {
        // Crear transacción
        await Transaction.create({
          walletId: parentWallet.id,
          type: 'earning_referral',
          amount: REFERRAL_BONUS,
          status: 'completed',
          description: `Bono por registro de referido: ${user.username}`
        }, { transaction: t });

        // Actualizar balance
        parentWallet.balance = parseFloat(parentWallet.balance) + REFERRAL_BONUS;
        await parentWallet.save({ transaction: t });
      }
    }

    await ActivityLog.create({
        userId: user.id,
        action: 'REGISTER',
        ipAddress: req.ip,
        details: JSON.stringify({ email: user.email })
    }, { transaction: t });

    await t.commit();

    res.status(201).json({
      id: user.id,
      username: user.username,
      email: user.email,
      role: user.role,
      referralCode: user.referralCode,
      token: generateToken(user.id)
    });

  } catch (error) {
    await t.rollback();
    console.error(error);
    res.status(500).json({ message: 'Error en el servidor', error: error.message });
  }
};

exports.login = async (req, res) => {
  const { email, password } = req.body;

  try {
    const user = await User.findOne({ where: { email } });

    if (!user) {
      await ActivityLog.create({
          userId: null,
          action: 'LOGIN_FAILED',
          ipAddress: req.ip,
          details: JSON.stringify({ email, reason: 'User not found' })
      });
      return res.status(401).json({ message: 'El correo electrónico no está registrado' });
    }

    if (!(await bcrypt.compare(password, user.password))) {
      await ActivityLog.create({
          userId: user.id,
          action: 'LOGIN_FAILED',
          ipAddress: req.ip,
          details: JSON.stringify({ email, reason: 'Invalid password' })
      });
      return res.status(401).json({ message: 'La contraseña es incorrecta' });
    }

    // Login exitoso
    await ActivityLog.create({
        userId: user.id,
        action: 'LOGIN',
        ipAddress: req.ip
    });

    res.json({
      id: user.id,
      username: user.username,
      email: user.email,
      role: user.role,
      referralCode: user.referralCode,
      token: generateToken(user.id)
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error en el servidor' });
  }
};

exports.getMe = async (req, res) => {
  try {
    const user = await User.findByPk(req.user.id, {
      attributes: { exclude: ['password'] },
      include: [{ model: Wallet, as: 'wallet' }]
    });
    res.json(user);
  } catch (error) {
    res.status(500).json({ message: 'Error al obtener perfil' });
  }
};

exports.forgotPassword = async (req, res) => {
  const { email } = req.body;

  try {
    const user = await User.findOne({ where: { email } });

    if (!user) {
      return res.status(404).json({ message: 'No existe usuario con ese email' });
    }

    // Generar token
    const resetToken = crypto.randomBytes(20).toString('hex');

    // Hash token y guardar en BD
    user.resetPasswordToken = crypto
      .createHash('sha256')
      .update(resetToken)
      .digest('hex');

    // Expiración 10 minutos
    user.resetPasswordExpire = Date.now() + 10 * 60 * 1000;

    await user.save();

    // Crear URL de reset
    const resetUrl = `${process.env.FRONTEND_URL || 'http://localhost:5173'}/reset-password/${resetToken}`;

    const message = `Has solicitado restablecer tu contraseña. Por favor ve a este link: \n\n ${resetUrl}`;

    try {
      await sendEmail({
        email: user.email,
        subject: 'Recuperación de contraseña',
        message
      });

      await ActivityLog.create({
          userId: user.id,
          action: 'FORGOT_PASSWORD_REQUEST',
          ipAddress: req.ip
      });

      res.status(200).json({ success: true, data: 'Email enviado' });
    } catch (error) {
      console.error(error);
      user.resetPasswordToken = null;
      user.resetPasswordExpire = null;
      await user.save();

      return res.status(500).json({ message: 'El email no pudo ser enviado' });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error en el servidor' });
  }
};

exports.resetPassword = async (req, res) => {
  // Obtener token hasheado
  const resetPasswordToken = crypto
    .createHash('sha256')
    .update(req.params.resettoken)
    .digest('hex');

  try {
    const user = await User.findOne({
      where: {
        resetPasswordToken,
        resetPasswordExpire: { [Op.gt]: Date.now() }
      }
    });

    if (!user) {
      return res.status(400).json({ message: 'Token inválido o expirado' });
    }

    // Setear nueva password
    // El hook beforeUpdate se encargará de hashear la contraseña porque estamos seteandola directamente
    // Nota: para que beforeUpdate funcione con instance.save(), debemos modificar la instancia
    user.password = req.body.password;
    user.resetPasswordToken = null;
    user.resetPasswordExpire = null;
    
    await user.save();

    await ActivityLog.create({
        userId: user.id,
        action: 'PASSWORD_RESET',
        ipAddress: req.ip
    });

    res.status(200).json({
      success: true,
      data: 'Contraseña actualizada',
      token: generateToken(user.id)
    });

  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error en el servidor' });
  }
};

exports.updateDetails = async (req, res) => {
  const { username, email } = req.body;

  try {
    const user = await User.findByPk(req.user.id);

    if (user) {
      user.username = username || user.username;
      user.email = email || user.email;

      const updatedUser = await user.save();
      
      await ActivityLog.create({
        userId: user.id,
        action: 'PROFILE_UPDATE',
        ipAddress: req.ip,
        details: JSON.stringify({ username: user.username, email: user.email })
      });

      res.json({
        id: updatedUser.id,
        username: updatedUser.username,
        email: updatedUser.email,
        role: updatedUser.role,
        referralCode: updatedUser.referralCode,
        token: generateToken(updatedUser.id)
      });
    } else {
      res.status(404).json({ message: 'Usuario no encontrado' });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error al actualizar perfil' });
  }
};

exports.updatePassword = async (req, res) => {
  const { currentPassword, newPassword } = req.body;

  try {
    const user = await User.findByPk(req.user.id);

    if (user && (await bcrypt.compare(currentPassword, user.password))) {
      user.password = newPassword;
      await user.save();

      await ActivityLog.create({
        userId: user.id,
        action: 'PASSWORD_CHANGE',
        ipAddress: req.ip
      });

      res.json({ message: 'Contraseña actualizada correctamente' });
    } else {
      res.status(401).json({ message: 'Contraseña actual incorrecta' });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error al actualizar contraseña' });
  }
};
