/**
 * Auth Middleware - Middleware de Autenticación
 *
 * Middleware para Express que valida JWT y extrae información del usuario.
 * Configura el tenant_id para RLS en PostgreSQL.
 *
 * @module Auth
 */

import { Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { AuthService } from '../services/auth.service';
import { TokenPayload } from '../dto/auth.dto';

// Extender Request de Express con información de autenticación
declare global {
  namespace Express {
    interface Request {
      user?: TokenPayload;
      tenantId?: string;
    }
  }
}

export class AuthMiddleware {
  constructor(
    private readonly authService: AuthService,
    private readonly dataSource: DataSource
  ) {}

  /**
   * Middleware de autenticación requerida
   */
  authenticate = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const token = this.extractToken(req);

      if (!token) {
        res.status(401).json({
          error: 'Unauthorized',
          message: 'No token provided',
        });
        return;
      }

      const validation = this.authService.validateAccessToken(token);

      if (!validation.valid || !validation.payload) {
        res.status(401).json({
          error: 'Unauthorized',
          message: validation.error || 'Invalid token',
        });
        return;
      }

      // Establecer información en el request
      req.user = validation.payload;
      req.tenantId = validation.payload.tenantId;

      // Configurar tenant_id para RLS en PostgreSQL
      await this.setTenantContext(validation.payload.tenantId);

      next();
    } catch (error) {
      res.status(401).json({
        error: 'Unauthorized',
        message: 'Authentication failed',
      });
    }
  };

  /**
   * Middleware de autenticación opcional
   */
  optionalAuthenticate = async (req: Request, _res: Response, next: NextFunction): Promise<void> => {
    try {
      const token = this.extractToken(req);

      if (token) {
        const validation = this.authService.validateAccessToken(token);

        if (validation.valid && validation.payload) {
          req.user = validation.payload;
          req.tenantId = validation.payload.tenantId;
          await this.setTenantContext(validation.payload.tenantId);
        }
      }

      next();
    } catch {
      // Si hay error, continuar sin autenticación
      next();
    }
  };

  /**
   * Middleware de autorización por roles
   */
  authorize = (...allowedRoles: string[]) => {
    return (req: Request, res: Response, next: NextFunction): void => {
      if (!req.user) {
        res.status(401).json({
          error: 'Unauthorized',
          message: 'Authentication required',
        });
        return;
      }

      const hasRole = req.user.roles.some((role) => allowedRoles.includes(role));

      if (!hasRole) {
        res.status(403).json({
          error: 'Forbidden',
          message: 'Insufficient permissions',
        });
        return;
      }

      next();
    };
  };

  /**
   * Middleware que requiere rol de admin
   */
  requireAdmin = (req: Request, res: Response, next: NextFunction): void => {
    return this.authorize('admin', 'super_admin')(req, res, next);
  };

  /**
   * Middleware que requiere ser supervisor
   */
  requireSupervisor = (req: Request, res: Response, next: NextFunction): void => {
    return this.authorize('admin', 'super_admin', 'supervisor_obra', 'supervisor_hse')(req, res, next);
  };

  /**
   * Extraer token del header Authorization
   */
  private extractToken(req: Request): string | null {
    const authHeader = req.headers.authorization;

    if (!authHeader) {
      return null;
    }

    // Bearer token
    const [type, token] = authHeader.split(' ');

    if (type !== 'Bearer' || !token) {
      return null;
    }

    return token;
  }

  /**
   * Configurar contexto de tenant para RLS
   */
  private async setTenantContext(tenantId: string): Promise<void> {
    try {
      await this.dataSource.query(`SET app.current_tenant_id = '${tenantId}'`);
    } catch (error) {
      console.error('Error setting tenant context:', error);
      throw new Error('Failed to set tenant context');
    }
  }
}

/**
 * Factory para crear middleware de autenticación
 */
export function createAuthMiddleware(
  authService: AuthService,
  dataSource: DataSource
): AuthMiddleware {
  return new AuthMiddleware(authService, dataSource);
}
