import type { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import * as authService from '../services/auth.service.js';
import { AppError } from '../middlewares/error.middleware.js';

const registerSchema = z.object({
  empresa: z.object({
    nombre: z.string().min(2, 'Nombre de empresa requerido'),
    rfc: z.string().min(12).max(13, 'RFC inválido'),
  }),
  usuario: z.object({
    nombre: z.string().min(2, 'Nombre requerido'),
    email: z.string().email('Email inválido'),
    password: z.string().min(8, 'La contraseña debe tener al menos 8 caracteres'),
  }),
});

const loginSchema = z.object({
  email: z.string().email('Email inválido'),
  password: z.string().min(1, 'Contraseña requerida'),
});

export async function register(req: Request, res: Response, next: NextFunction) {
  try {
    const data = registerSchema.parse(req.body);
    const result = await authService.register(data);
    res.status(201).json(result);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return next(new AppError(400, error.errors[0].message));
    }
    next(error);
  }
}

export async function login(req: Request, res: Response, next: NextFunction) {
  try {
    const data = loginSchema.parse(req.body);
    const result = await authService.login(data);
    res.json(result);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return next(new AppError(400, error.errors[0].message));
    }
    next(error);
  }
}

export async function refresh(req: Request, res: Response, next: NextFunction) {
  try {
    const { refreshToken } = req.body;
    if (!refreshToken) {
      throw new AppError(400, 'Refresh token requerido');
    }
    const tokens = await authService.refreshTokens(refreshToken);
    res.json(tokens);
  } catch (error) {
    next(error);
  }
}

export async function logout(req: Request, res: Response, next: NextFunction) {
  try {
    const { refreshToken } = req.body;
    if (refreshToken) {
      await authService.logout(refreshToken);
    }
    res.json({ message: 'Sesión cerrada exitosamente' });
  } catch (error) {
    next(error);
  }
}

export async function me(req: Request, res: Response, next: NextFunction) {
  try {
    res.json({ user: req.user });
  } catch (error) {
    next(error);
  }
}

const passwordResetRequestSchema = z.object({
  email: z.string().email('Email inválido'),
});

const passwordResetConfirmSchema = z.object({
  token: z.string().min(10, 'Token inválido'),
  newPassword: z.string().min(8, 'La contraseña debe tener al menos 8 caracteres'),
});

/**
 * Solicita recuperación de contraseña. Responde 200 siempre (anti-enumeration),
 * independiente de si el email existe o no.
 */
export async function requestPasswordReset(req: Request, res: Response, next: NextFunction) {
  try {
    const { email } = passwordResetRequestSchema.parse(req.body);
    // Dispara async — no esperamos resultado para preservar timing constante
    await authService.requestPasswordReset(email);
    res.json({
      message: 'Si el email existe en nuestro sistema, recibirás un enlace para restablecer tu contraseña.',
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return next(new AppError(400, error.errors[0].message));
    }
    next(error);
  }
}

/**
 * Confirma recuperación con token + nueva contraseña.
 */
export async function confirmPasswordReset(req: Request, res: Response, next: NextFunction) {
  try {
    const { token, newPassword } = passwordResetConfirmSchema.parse(req.body);
    await authService.confirmPasswordReset(token, newPassword);
    res.json({ message: 'Contraseña actualizada exitosamente. Por favor inicia sesión con tu nueva contraseña.' });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return next(new AppError(400, error.errors[0].message));
    }
    next(error);
  }
}

const changePasswordSchema = z.object({
  currentPassword: z.string().min(1, 'Contraseña actual requerida'),
  newPassword: z.string().min(8, 'La contraseña debe tener al menos 8 caracteres'),
});

/**
 * Cambia la contraseña del user autenticado. Requiere contraseña actual.
 * Tras cambio: todas las sesiones del user quedan invalidadas (incluyendo esta).
 */
export async function changePassword(req: Request, res: Response, next: NextFunction) {
  try {
    const { currentPassword, newPassword } = changePasswordSchema.parse(req.body);
    await authService.changePassword({
      userId: req.user!.userId,
      currentPassword,
      newPassword,
    });
    res.json({
      message: 'Contraseña actualizada. Por seguridad, todas tus sesiones fueron cerradas. Inicia sesión de nuevo.',
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return next(new AppError(400, error.errors[0].message));
    }
    next(error);
  }
}

/**
 * "Cerrar todas las sesiones" — invalida todos los tokens del user actual.
 */
export async function logoutAll(req: Request, res: Response, next: NextFunction) {
  try {
    await authService.logoutAllSessions(req.user!.userId);
    res.json({ message: 'Todas tus sesiones fueron cerradas. Inicia sesión de nuevo.' });
  } catch (error) {
    next(error);
  }
}

const switchTenantSchema = z.object({
  tenantId: z.string().uuid('tenantId inválido'),
  refreshToken: z.string().min(1, 'refreshToken requerido'),
});

/**
 * Cambia el tenant activo del user (requiere membership válida). Emite un par
 * nuevo de tokens apuntando al tenant destino y revoca el refresh token actual.
 */
export async function switchTenant(req: Request, res: Response, next: NextFunction) {
  try {
    const { tenantId, refreshToken } = switchTenantSchema.parse(req.body);
    const result = await authService.switchTenant({
      userId: req.user!.userId,
      currentRefreshToken: refreshToken,
      targetTenantId: tenantId,
    });
    res.json(result);
  } catch (error) {
    if (error instanceof z.ZodError) return next(new AppError(400, error.errors[0].message));
    next(error);
  }
}
