import { ExecutionContext } from '@nestjs/common';
import { User } from './user.decorator';
import { AuthenticatedUser } from '../interfaces/jwt-payload.interface';

describe('User Decorator', () => {
  let mockExecutionContext: ExecutionContext;
  let mockRequest: any;

  beforeEach(() => {
    mockRequest = {
      user: null,
    };

    mockExecutionContext = {
      switchToHttp: jest.fn().mockReturnValue({
        getRequest: jest.fn().mockReturnValue(mockRequest),
      }),
    } as any;
  });

  describe('User', () => {
    const mockUser: AuthenticatedUser = {
      id: '123',
      type: 'user',
      payload: {
        iss: 'test-issuer',
        iat: 1234567890,
        exp: 1234567890,
        nbf: 1234567890,
        sub: '123',
        jti: 'test-jti',
        prv: 'users',
      },
      expiresAt: new Date('2024-12-31'),
      issuedAt: new Date('2024-01-01'),
      tokenData: {
        role: 'admin',
        permissions: ['read', 'write'],
        department: 'IT',
      },
      rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
    };

    it('should return entire user when no field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = undefined;
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toEqual(mockUser);
    });

    it('should return specific field when field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'id';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toBe('123');
    });

    it('should return user type when type field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'type';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toBe('user');
    });

    it('should return payload when payload field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'payload';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toEqual({
        iss: 'test-issuer',
        iat: 1234567890,
        exp: 1234567890,
        nbf: 1234567890,
        sub: '123',
        jti: 'test-jti',
        prv: 'users',
      });
    });

    it('should return tokenData when tokenData field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'tokenData';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toEqual({
        role: 'admin',
        permissions: ['read', 'write'],
        department: 'IT',
      });
    });

    it('should return rawToken when rawToken field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'rawToken';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toBe('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
    });

    it('should return expiresAt when expiresAt field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'expiresAt';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toEqual(new Date('2024-12-31'));
    });

    it('should return issuedAt when issuedAt field is specified', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'issuedAt';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toEqual(new Date('2024-01-01'));
    });

    it('should return undefined when field does not exist', () => {
      mockRequest.user = mockUser;

      // Simulate User decorator logic
      const data = 'nonExistentField';
      const user = mockUser;
      const result = data ? user?.[data] : user;
      expect(result).toBeUndefined();
    });

    it('should return undefined when user is null', () => {
      mockRequest.user = null;

      // Simulate User decorator logic
      const data = 'id';
      const user = null;
      const result = data ? user?.[data] : user;
      expect(result).toBeUndefined();
    });

    it('should return undefined when user is undefined', () => {
      mockRequest.user = undefined;

      // Simulate User decorator logic
      const data = 'id';
      const user = undefined;
      const result = data ? user?.[data] : user;
      expect(result).toBeUndefined();
    });

    it('should return null when user is null and no field is specified', () => {
      mockRequest.user = null;

      // Simulate User decorator logic
      const data = undefined;
      const user = null;
      const result = data ? user?.[data] : user;
      expect(result).toBeNull();
    });

    it('should return undefined when user is undefined and no field is specified', () => {
      mockRequest.user = undefined;

      // Simulate User decorator logic
      const data = undefined;
      const user = undefined;
      const result = data ? user?.[data] : user;
      expect(result).toBeUndefined();
    });
  });

  describe('Edge cases', () => {
    it('should handle user with empty tokenData', () => {
      const userWithEmptyTokenData: AuthenticatedUser = {
        id: '123',
        type: 'user',
        payload: {
          iss: 'test-issuer',
          iat: 1234567890,
          exp: 1234567890,
          nbf: 1234567890,
          sub: '123',
          jti: 'test-jti',
          prv: 'users',
        },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        tokenData: {},
        rawToken: null,
      };

      mockRequest.user = userWithEmptyTokenData;

      // Simulate User decorator logic
      const tokenDataResult = userWithEmptyTokenData?.['tokenData'];
      expect(tokenDataResult).toEqual({});

      const rawTokenResult = userWithEmptyTokenData?.['rawToken'];
      expect(rawTokenResult).toBeNull();
    });

    it('should handle client type user', () => {
      const clientUser: AuthenticatedUser = {
        id: '456',
        type: 'client',
        payload: {
          iss: 'test-issuer',
          iat: 1234567890,
          exp: 1234567890,
          nbf: 1234567890,
          sub: '456',
          jti: 'test-jti',
          prv: 'clients',
        },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        tokenData: {
          permissions: ['read'],
          clientType: 'premium',
        },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = clientUser;

      // Simulate User decorator logic
      const typeResult = clientUser?.['type'];
      expect(typeResult).toBe('client');

      const idResult = clientUser?.['id'];
      expect(idResult).toBe('456');

      const tokenDataResult = clientUser?.['tokenData'];
      expect(tokenDataResult).toEqual({
        permissions: ['read'],
        clientType: 'premium',
      });
    });

    it('should handle user with empty tokenData object', () => {
      const userWithEmptyTokenData: AuthenticatedUser = {
        id: '123',
        type: 'user',
        payload: {
          iss: 'test-issuer',
          iat: 1234567890,
          exp: 1234567890,
          nbf: 1234567890,
          sub: '123',
          jti: 'test-jti',
          prv: 'users',
        },
        expiresAt: new Date('2024-12-31'),
        issuedAt: new Date('2024-01-01'),
        tokenData: {},
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = userWithEmptyTokenData;

      // Simulate User decorator logic
      const tokenDataResult = userWithEmptyTokenData?.['tokenData'];
      expect(tokenDataResult).toEqual({});
    });
  });
});
