import { logger } from '@goodgo/logger';
import { ApiResponse } from '@goodgo/types';
import { Request, Response, NextFunction } from 'express';

import { jwtService } from '../modules/token/jwt.service';
import { getErrorMessage } from '../utils/error-utils';

/**
 * EN: Extended Request interface with user information
 * VI: Interface Request mở rộng với thông tin người dùng
 */
declare global {
   
  namespace Express {
    interface Request {
      user?: {
        id?: string;
        userId?: string;
        sub?: string;
        email: string;
        role?: string;
        roles?: string[];
        permissions?: string[];
        iat?: number;
        exp?: number;
      };
    }
  }
}

/**
 * EN: Authentication middleware - verifies JWT tokens
 * VI: Middleware xác thực - xác minh JWT tokens
 *
 * @param options - Configuration options / Tùy chọn cấu hình
 */
export const authenticate = (_options: {
  secret?: string;
  ignoreExpiration?: boolean;
} = {}) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      // EN: Extract token from Authorization header or cookie
      // VI: Trích xuất token từ header Authorization hoặc cookie
      let token: string | null = null;

      // Try Authorization header first
      const authHeader = req.headers.authorization;
      if (authHeader && authHeader.startsWith('Bearer ')) {
        token = authHeader.substring(7);
      }

      // Try cookie if no header token
      if (!token && req.cookies) {
        token = req.cookies.access_token || null;
      }

      if (!token) {
        logger.warn('No authentication token provided / Không có token xác thực được cung cấp', {
          path: req.path,
          method: req.method,
        });

        const response: ApiResponse = {
          success: false,
          error: {
            code: 'AUTH_001',
            message: 'Authentication required / Yêu cầu xác thực',
          },
          timestamp: new Date().toISOString(),
        };
        res.status(401).json(response);
        return;
      }

      // EN: Verify token using JWT service
      // VI: Xác minh token sử dụng JWT service
      const payload = await jwtService.verifyAccessToken(token);

      // EN: Attach user information to request
      // VI: Gắn thông tin người dùng vào request
      req.user = {
        id: payload.sub,
        userId: payload.sub,
        sub: payload.sub,
        email: payload.email,
        role: payload.roles?.[0], // Primary role
        roles: payload.roles,
        permissions: payload.permissions,
        iat: payload.iat,
        exp: payload.exp,
      };

      logger.debug('User authenticated successfully / Người dùng đã được xác thực thành công', {
        userId: payload.sub,
        email: payload.email,
        roles: payload.roles,
      });

      next();
    } catch (error: unknown) {
      logger.warn('Authentication failed / Xác thực thất bại', {
        error: getErrorMessage(error),
        path: req.path,
        method: req.method,
      });

      const response: ApiResponse = {
        success: false,
        error: {
          code: 'AUTH_002',
          message: 'Invalid or expired token / Token không hợp lệ hoặc hết hạn',
        },
        timestamp: new Date().toISOString(),
      };
      res.status(401).json(response);
    }
  };
};

/**
 * EN: Role-based authorization middleware
 * VI: Middleware phân quyền dựa trên vai trò
 *
 * @param allowedRoles - Array of roles that can access the resource / Mảng các vai trò được phép truy cập tài nguyên
 */
export const authorize = (...allowedRoles: string[]) => {
  return (req: Request, res: Response, next: NextFunction) => {
    // EN: Check if user is authenticated
    // VI: Kiểm tra người dùng đã được xác thực chưa
    if (!req.user) {
      logger.warn('Authorization attempted without authentication / Phân quyền được thử mà không xác thực', {
        path: req.path,
        method: req.method,
      });

      const response: ApiResponse = {
        success: false,
        error: {
          code: 'AUTH_003',
          message: 'Authentication required / Yêu cầu xác thực',
        },
        timestamp: new Date().toISOString(),
      };
      res.status(401).json(response);
      return;
    }

    // EN: Check if user has required role
    // VI: Kiểm tra người dùng có vai trò cần thiết không
    const userRoles = req.user.roles || (req.user.role ? [req.user.role] : []);
    const hasRole = userRoles.some(role => allowedRoles.includes(role));

    if (!hasRole) {
      logger.warn('Access denied - insufficient permissions / Truy cập bị từ chối - không đủ quyền', {
        userId: req.user.userId || req.user.id,
        userRoles,
        requiredRoles: allowedRoles,
        path: req.path,
        method: req.method,
      });

      const response: ApiResponse = {
        success: false,
        error: {
          code: 'AUTH_004',
          message: 'Insufficient permissions / Không đủ quyền',
        },
        timestamp: new Date().toISOString(),
      };
      res.status(403).json(response);
      return;
    }

    logger.debug('Authorization successful / Phân quyền thành công', {
      userId: req.user.userId || req.user.id,
      userRoles,
      path: req.path,
      method: req.method,
    });

    next();
  };
};

/**
 * EN: Combined auth and authorization middleware
 * VI: Middleware kết hợp xác thực và phân quyền
 *
 * @param secret - JWT secret key / Khóa bí mật JWT
 * @param allowedRoles - Array of allowed roles / Mảng vai trò được phép
 */
export const requireAuth = (
  secret?: string,
  ...allowedRoles: string[]
) => {
  return [authenticate({ secret }), authorize(...allowedRoles)];
};

/**
 * EN: Optional authentication middleware - doesn't fail if no token provided
 * VI: Middleware xác thực tùy chọn - không thất bại nếu không có token
 *
 * @param options - Configuration options / Tùy chọn cấu hình
 */
export const optionalAuth = (_options: {
  secret?: string;
  ignoreExpiration?: boolean;
} = {}) => {
  return async (req: Request, _res: Response, next: NextFunction) => {
    try {
      let token: string | null = null;

      const authHeader = req.headers.authorization;
      if (authHeader && authHeader.startsWith('Bearer ')) {
        token = authHeader.substring(7);
      }

      if (!token && req.cookies) {
        token = req.cookies.access_token || null;
      }

      if (token) {
        const payload = await jwtService.verifyAccessToken(token);
        req.user = {
          id: payload.sub,
          userId: payload.sub,
          sub: payload.sub,
          email: payload.email,
          role: payload.roles?.[0],
          roles: payload.roles,
          permissions: payload.permissions,
          iat: payload.iat,
          exp: payload.exp,
        };

        logger.debug('Optional authentication successful / Xác thực tùy chọn thành công', {
          userId: payload.sub,
        });
      }

      next();
    } catch (error: unknown) {
      // EN: For optional auth, just continue without user info
      // VI: Với optional auth, chỉ tiếp tục mà không có thông tin user
      logger.debug('Optional authentication skipped / Xác thực tùy chọn bị bỏ qua', {
        reason: getErrorMessage(error),
      });
      next();
    }
  };
};

/**
 * EN: Check if user has specific role (utility function)
 * VI: Kiểm tra người dùng có vai trò cụ thể (hàm tiện ích)
 *
 * @param user - User object from request / Đối tượng user từ request
 * @param role - Role to check / Vai trò cần kiểm tra
 * @returns True if user has the role / True nếu user có vai trò
 */
export const hasRole = (user: Express.Request['user'], role: string): boolean => {
  if (!user) return false;
  const userRoles = user.roles || (user.role ? [user.role] : []);
  return userRoles.includes(role);
};

/**
 * EN: Check if user has any of the specified roles (utility function)
 * VI: Kiểm tra người dùng có bất kỳ vai trò nào trong danh sách (hàm tiện ích)
 *
 * @param user - User object from request / Đối tượng user từ request
 * @param roles - Array of roles to check / Mảng vai trò cần kiểm tra
 * @returns True if user has any of the roles / True nếu user có bất kỳ vai trò nào
 */
export const hasAnyRole = (user: Express.Request['user'], roles: string[]): boolean => {
  if (!user) return false;
  const userRoles = user.roles || (user.role ? [user.role] : []);
  return userRoles.some(r => roles.includes(r));
};

/**
 * EN: Check if user is authenticated (utility function)
 * VI: Kiểm tra người dùng đã được xác thực (hàm tiện ích)
 *
 * @param user - User object from request / Đối tượng user từ request
 * @returns True if user is authenticated / True nếu user đã được xác thực
 */
export const isAuthenticated = (user: Express.Request['user']): boolean => {
  return !!user;
};
