import { Test, TestingModule } from '@nestjs/testing';
import {
  ExecutionContext,
  UnauthorizedException,
  ForbiddenException,
} from '@nestjs/common';
import { UserJwtGuard } from './user-jwt.guard';
import { AuthenticatedUser } from '../interfaces/jwt-payload.interface';

describe('UserJwtGuard', () => {
  let guard: UserJwtGuard;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UserJwtGuard],
    }).compile();

    guard = module.get<UserJwtGuard>(UserJwtGuard);
  });

  it('should be defined', () => {
    expect(guard).toBeDefined();
  });

  describe('handleRequest', () => {
    let mockContext: ExecutionContext;

    beforeEach(() => {
      mockContext = {
        switchToHttp: jest.fn().mockReturnThis(),
        getRequest: jest.fn().mockReturnValue({
          headers: {
            authorization: 'Bearer test-token-123',
          },
        }),
      } as any;
    });

    it('should throw UnauthorizedException when there is an error', () => {
      const error = new Error('Token invalid');
      const user = null;
      const info = null;

      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow(UnauthorizedException);
      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow('Token de autorização inválido');
    });

    it('should throw UnauthorizedException when user is null', () => {
      const error = null;
      const user = null;
      const info = null;

      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow(UnauthorizedException);
      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow('Token não fornecido ou inválido');
    });

    it('should throw ForbiddenException when user type is client', () => {
      const error = null;
      const user: AuthenticatedUser = {
        id: 'client_123',
        type: 'client',
        payload: {
          sub: 'client_123',
          iss: 'test-issuer',
          prv: 'hash123',
          iat: 1640995200,
          exp: 1672531200,
          nbf: 1640995200,
          jti: 'jwt-id-123',
        },
        tokenData: { clientId: 'client_123' },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        rawToken: null,
      };
      const info = null;

      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow(ForbiddenException);
      expect(() => {
        guard.handleRequest(error, user, info, mockContext);
      }).toThrow('Acesso negado: token de client não permitido');
    });

    it('should return authenticated user when type is user', () => {
      const error = null;
      const user: AuthenticatedUser = {
        id: 'user_123',
        type: 'user',
        payload: {
          sub: 'user_123',
          iss: 'test-issuer',
          prv: 'users',
          iat: 1640995200,
          exp: 1672531200,
          nbf: 1640995200,
          jti: 'jwt-id-123',
        },
        tokenData: { userId: 'user_123' },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        rawToken: null,
      };
      const info = null;

      const result = guard.handleRequest(error, user, info, mockContext);

      expect(result).toEqual({
        ...user,
        rawToken: 'test-token-123',
      });
    });

    it('should handle request without authorization header', () => {
      const error = null;
      const user: AuthenticatedUser = {
        id: 'user_123',
        type: 'user',
        payload: {
          sub: 'user_123',
          iss: 'test-issuer',
          prv: 'users',
          iat: 1640995200,
          exp: 1672531200,
          nbf: 1640995200,
          jti: 'jwt-id-123',
        },
        tokenData: { userId: 'user_123' },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        rawToken: null,
      };
      const info = null;

      // Mock context without authorization header
      const contextWithoutAuth = {
        switchToHttp: jest.fn().mockReturnThis(),
        getRequest: jest.fn().mockReturnValue({
          headers: {},
        }),
      } as any;

      const result = guard.handleRequest(error, user, info, contextWithoutAuth);

      expect(result).toEqual({
        ...user,
        rawToken: null,
      });
    });

    it('should handle request with malformed authorization header', () => {
      const error = null;
      const user: AuthenticatedUser = {
        id: 'user_123',
        type: 'user',
        payload: {
          sub: 'user_123',
          iss: 'test-issuer',
          prv: 'users',
          iat: 1640995200,
          exp: 1672531200,
          nbf: 1640995200,
          jti: 'jwt-id-123',
        },
        tokenData: { userId: 'user_123' },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        rawToken: null,
      };
      const info = null;

      // Mock context with malformed authorization header
      const contextWithMalformedAuth = {
        switchToHttp: jest.fn().mockReturnThis(),
        getRequest: jest.fn().mockReturnValue({
          headers: {
            authorization: 'InvalidFormat token-123',
          },
        }),
      } as any;

      const result = guard.handleRequest(
        error,
        user,
        info,
        contextWithMalformedAuth,
      );

      expect(result).toEqual({
        ...user,
        rawToken: null,
      });
    });
  });
});
