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

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

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

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

  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 return authenticated user with raw token when valid', () => {
      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,
      });
    });

    it('should handle request with undefined headers', () => {
      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 undefined headers
      const contextWithUndefinedHeaders = {
        switchToHttp: jest.fn().mockReturnThis(),
        getRequest: jest.fn().mockReturnValue({}),
      } as any;

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

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