const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { validationResult } = require("express-validator");
const prisma = require("../models/prisma");
const logger = require("../utils/logger");

const generateTokens = (userId, role) => {
  const accessToken = jwt.sign(
    { userId, role },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_ACCESS_EXPIRES || "15m" }
  );
  const refreshToken = jwt.sign(
    { userId },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: process.env.JWT_REFRESH_EXPIRES || "7d" }
  );
  return { accessToken, refreshToken };
};

const login = async (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });

  const { email, password } = req.body;

  try {
    const user = await prisma.user.findUnique({ where: { email: email.toLowerCase() } });

    if (!user || !user.isActive) {
      return res.status(401).json({ error: "Credenciales invalidas" });
    }

    // Verificar si esta bloqueado
    if (user.lockedUntil && user.lockedUntil > new Date()) {
      const minutesLeft = Math.ceil((user.lockedUntil - new Date()) / 60000);
      return res.status(423).json({ error: "Cuenta bloqueada. Intenta en " + minutesLeft + " minutos." });
    }

    const passwordValid = await bcrypt.compare(password, user.passwordHash);

    if (!passwordValid) {
      const maxAttempts = parseInt(process.env.MAX_LOGIN_ATTEMPTS) || 5;
      const newAttempts = user.loginAttempts + 1;
      const lockData = newAttempts >= maxAttempts
        ? { loginAttempts: newAttempts, lockedUntil: new Date(Date.now() + parseInt(process.env.LOCK_TIME_MINUTES || 15) * 60000) }
        : { loginAttempts: newAttempts };

      await prisma.user.update({ where: { id: user.id }, data: lockData });
      return res.status(401).json({ error: "Credenciales invalidas" });
    }

    // Reset intentos fallidos
    await prisma.user.update({
      where: { id: user.id },
      data: { loginAttempts: 0, lockedUntil: null, lastLoginAt: new Date(), lastLoginIp: req.ip },
    });

    const { accessToken, refreshToken } = generateTokens(user.id, user.role);

    // Guardar refresh token en BD
    await prisma.refreshToken.create({
      data: {
        token: refreshToken,
        userId: user.id,
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        ipAddress: req.ip,
        userAgent: req.headers["user-agent"],
      },
    });

    // Audit log
    await prisma.auditLog.create({
      data: { userId: user.id, action: "LOGIN", entity: "users", entityId: user.id, ipAddress: req.ip },
    });

    res.json({
      accessToken,
      refreshToken,
      user: { id: user.id, email: user.email, firstName: user.firstName, lastName: user.lastName, role: user.role },
    });
  } catch (error) {
    logger.error("Error en login:", error);
    res.status(500).json({ error: "Error interno del servidor" });
  }
};

const refresh = async (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) return res.status(400).json({ error: "Refresh token requerido" });

  try {
    const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);

    const storedToken = await prisma.refreshToken.findUnique({
      where: { token: refreshToken },
      include: { user: true },
    });

    if (!storedToken || storedToken.revokedAt || storedToken.expiresAt < new Date()) {
      return res.status(401).json({ error: "Refresh token invalido o expirado" });
    }

    if (!storedToken.user.isActive) {
      return res.status(401).json({ error: "Usuario inactivo" });
    }

    // Revocar token antiguo y emitir nuevos
    await prisma.refreshToken.update({ where: { id: storedToken.id }, data: { revokedAt: new Date() } });

    const tokens = generateTokens(storedToken.user.id, storedToken.user.role);

    await prisma.refreshToken.create({
      data: {
        token: tokens.refreshToken,
        userId: storedToken.user.id,
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        ipAddress: req.ip,
        userAgent: req.headers["user-agent"],
      },
    });

    res.json(tokens);
  } catch (error) {
    res.status(401).json({ error: "Refresh token invalido" });
  }
};

const logout = async (req, res) => {
  const { refreshToken } = req.body;

  if (refreshToken) {
    await prisma.refreshToken.updateMany({
      where: { token: refreshToken, userId: req.user.id },
      data: { revokedAt: new Date() },
    }).catch(() => {});
  }

  await prisma.auditLog.create({
    data: { userId: req.user.id, action: "LOGOUT", entity: "users", entityId: req.user.id, ipAddress: req.ip },
  }).catch(() => {});

  res.json({ message: "Sesion cerrada exitosamente" });
};

const me = async (req, res) => {
  const user = await prisma.user.findUnique({
    where: { id: req.user.id },
    select: { id: true, email: true, firstName: true, lastName: true, role: true, isActive: true, phone: true, department: true, jobTitle: true, avatarUrl: true, lastLoginAt: true, createdAt: true },
  });
  res.json(user);
};

module.exports = { login, refresh, logout, me };
