/**
 * AuthController - Controlador de Autenticación
 *
 * Endpoints REST para login, register, refresh y logout.
 * Implementa validación de datos y manejo de errores.
 *
 * @module Auth
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { AuthService } from '../services/auth.service';
import { AuthMiddleware } from '../middleware/auth.middleware';
import { User } from '../../core/entities/user.entity';
import { Tenant } from '../../core/entities/tenant.entity';
import { RefreshToken } from '../entities/refresh-token.entity';
import {
  LoginDto,
  RegisterDto,
  RefreshTokenDto,
  ChangePasswordDto,
} from '../dto/auth.dto';

/**
 * Crear router de autenticación
 */
export function createAuthController(dataSource: DataSource): Router {
  const router = Router();

  // Inicializar repositorios
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);

  // Inicializar servicio
  const authService = new AuthService(
    userRepository,
    tenantRepository,
    refreshTokenRepository as any
  );

  // Inicializar middleware
  const authMiddleware = new AuthMiddleware(authService, dataSource);

  /**
   * POST /auth/login
   * Login de usuario
   */
  router.post('/login', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: LoginDto = req.body;

      if (!dto.email || !dto.password) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'Email and password are required',
        });
        return;
      }

      const result = await authService.login(dto);
      res.status(200).json({ success: true, data: result });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === 'Invalid credentials') {
          res.status(401).json({ error: 'Unauthorized', message: 'Invalid email or password' });
          return;
        }
        if (error.message === 'User is not active') {
          res.status(403).json({ error: 'Forbidden', message: 'User account is disabled' });
          return;
        }
        if (error.message === 'No tenant specified' || error.message === 'Tenant not found or inactive') {
          res.status(400).json({ error: 'Bad Request', message: error.message });
          return;
        }
      }
      next(error);
    }
  });

  /**
   * POST /auth/register
   * Registro de nuevo usuario
   */
  router.post('/register', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: RegisterDto = req.body;

      if (!dto.email || !dto.password || !dto.firstName || !dto.lastName || !dto.tenantId) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'Email, password, firstName, lastName and tenantId are required',
        });
        return;
      }

      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(dto.email)) {
        res.status(400).json({ error: 'Bad Request', message: 'Invalid email format' });
        return;
      }

      if (dto.password.length < 8) {
        res.status(400).json({ error: 'Bad Request', message: 'Password must be at least 8 characters' });
        return;
      }

      const result = await authService.register(dto);
      res.status(201).json({ success: true, data: result });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === 'Email already registered') {
          res.status(409).json({ error: 'Conflict', message: 'Email is already registered' });
          return;
        }
        if (error.message === 'Tenant not found') {
          res.status(400).json({ error: 'Bad Request', message: 'Invalid tenant ID' });
          return;
        }
      }
      next(error);
    }
  });

  /**
   * POST /auth/refresh
   * Renovar access token usando refresh token
   */
  router.post('/refresh', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: RefreshTokenDto = req.body;

      if (!dto.refreshToken) {
        res.status(400).json({ error: 'Bad Request', message: 'Refresh token is required' });
        return;
      }

      const result = await authService.refresh(dto);
      res.status(200).json({ success: true, data: result });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === 'Invalid refresh token' || error.message === 'Refresh token expired or revoked') {
          res.status(401).json({ error: 'Unauthorized', message: error.message });
          return;
        }
        if (error.message === 'User not found or inactive') {
          res.status(401).json({ error: 'Unauthorized', message: 'User account is disabled or deleted' });
          return;
        }
      }
      next(error);
    }
  });

  /**
   * POST /auth/logout
   * Cerrar sesión (revocar refresh token)
   */
  router.post('/logout', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const { refreshToken } = req.body;
      if (refreshToken) {
        await authService.logout(refreshToken);
      }
      res.status(200).json({ success: true, message: 'Logged out successfully' });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /auth/change-password
   * Cambiar contraseña (requiere autenticación)
   */
  router.post('/change-password', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: ChangePasswordDto = req.body;
      const userId = req.user?.sub;

      if (!userId) {
        res.status(401).json({ error: 'Unauthorized', message: 'User not authenticated' });
        return;
      }

      if (!dto.currentPassword || !dto.newPassword) {
        res.status(400).json({ error: 'Bad Request', message: 'Current password and new password are required' });
        return;
      }

      if (dto.newPassword.length < 8) {
        res.status(400).json({ error: 'Bad Request', message: 'New password must be at least 8 characters' });
        return;
      }

      await authService.changePassword(userId, dto);
      res.status(200).json({ success: true, message: 'Password changed successfully' });
    } catch (error) {
      if (error instanceof Error && error.message === 'Current password is incorrect') {
        res.status(400).json({ error: 'Bad Request', message: 'Current password is incorrect' });
        return;
      }
      next(error);
    }
  });

  /**
   * GET /auth/me
   * Obtener información del usuario autenticado
   */
  router.get('/me', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const userId = req.user?.sub;

      if (!userId) {
        res.status(401).json({ error: 'Unauthorized', message: 'User not authenticated' });
        return;
      }

      const user = await userRepository.findOne({
        where: { id: userId } as any,
        select: ['id', 'email', 'firstName', 'lastName', 'isActive', 'createdAt'],
      });

      if (!user) {
        res.status(404).json({ error: 'Not Found', message: 'User not found' });
        return;
      }

      res.status(200).json({
        success: true,
        data: {
          id: user.id,
          email: user.email,
          firstName: user.firstName,
          lastName: user.lastName,
          roles: req.user?.roles || [],
          tenantId: req.tenantId,
        },
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /auth/verify
   * Verificar si el token es válido
   */
  router.get('/verify', authMiddleware.authenticate, (req: Request, res: Response): void => {
    res.status(200).json({
      success: true,
      data: {
        valid: true,
        user: {
          id: req.user?.sub,
          email: req.user?.email,
          roles: req.user?.roles,
        },
        tenantId: req.tenantId,
      },
    });
  });

  return router;
}

export default createAuthController;
