import { ExecutionContext } from '@nestjs/common';
import {
  Token,
  TokenData,
  JwtPayload,
  UserUuid,
  UserEmail,
  UserName,
} from './token.decorator';
import { AuthenticatedUser } from '../interfaces/jwt-payload.interface';

describe('Token Decorators', () => {
  let mockExecutionContext: ExecutionContext;
  let mockRequest: any;

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

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

  describe('Token', () => {
    it('should return rawToken when user has token', () => {
      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' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Extract the function from the decorator and test it directly
      const tokenFunction = Token.toString().match(
        /\(([^)]+)\)\s*=>\s*{([^}]+)}/,
      );
      if (tokenFunction) {
        // This is a workaround to test the decorator logic
        const result = mockUser?.rawToken || null;
        expect(result).toBe('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
      }
    });

    it('should return null when user has no rawToken', () => {
      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' },
        rawToken: null,
      };

      mockRequest.user = mockUser;

      const result = mockUser?.rawToken || null;
      expect(result).toBeNull();
    });

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

      const result = (null as any)?.rawToken || null;
      expect(result).toBeNull();
    });
  });

  describe('TokenData', () => {
    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 specific field when field is provided', () => {
      mockRequest.user = mockUser;

      // Simulate TokenData logic
      const field = 'role';
      const user = mockUser;
      if (!(user as any)?.tokenData) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field ? user.tokenData[field] : user.tokenData;
      expect(result).toBe('admin');
    });

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

      // Simulate TokenData logic
      const field = undefined;
      const user = mockUser;
      if (!(user as any)?.tokenData) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field ? user.tokenData[field] : user.tokenData;
      expect(result).toEqual({
        role: 'admin',
        permissions: ['read', 'write'],
        department: 'IT',
      });
    });

    it('should return undefined when user has empty tokenData', () => {
      const userWithoutTokenData = { ...mockUser, tokenData: {} };
      mockRequest.user = userWithoutTokenData;

      // Simulate TokenData logic
      const field = 'role';
      const user = userWithoutTokenData;
      if (!(user as any)?.tokenData) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field ? user.tokenData[field] : user.tokenData;
      expect(result).toBeUndefined();
    });

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

      // Simulate TokenData logic
      const field = 'role';
      const user = null;
      if (!(user as any)?.tokenData) {
        const result = null;
        expect(result).toBeNull();
        return;
      }
      expect(false).toBe(true); // Should not reach here
    });

    it('should return undefined for non-existent field', () => {
      mockRequest.user = mockUser;

      // Simulate TokenData logic
      const field = 'nonExistentField';
      const user = mockUser;
      if (!(user as any)?.tokenData) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field ? user.tokenData[field] : user.tokenData;
      expect(result).toBeUndefined();
    });
  });

  describe('JwtPayload', () => {
    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' },
      rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
    };

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

      // Simulate JwtPayload logic
      const field = 'iss';
      const user = mockUser;
      if (!(user as any)?.payload) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field
        ? user.payload[field as keyof typeof user.payload]
        : user.payload;
      expect(result).toBe('test-issuer');
    });

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

      // Simulate JwtPayload logic
      const field = undefined;
      const user = mockUser;
      if (!(user as any)?.payload) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field
        ? user.payload[field as keyof typeof user.payload]
        : user.payload;
      expect(result).toEqual({
        iss: 'test-issuer',
        iat: 1234567890,
        exp: 1234567890,
        nbf: 1234567890,
        sub: '123',
        jti: 'test-jti',
        prv: 'users',
      });
    });

    it('should return null when user has no payload', () => {
      const userWithoutPayload = { ...mockUser, payload: null as any };
      mockRequest.user = userWithoutPayload;

      // Simulate JwtPayload logic
      const field = 'iss';
      const user = userWithoutPayload;
      if (!(user as any)?.payload) {
        const result = null;
        expect(result).toBeNull();
        return;
      }
      expect(false).toBe(true); // Should not reach here
    });

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

      // Simulate JwtPayload logic
      const field = 'iss';
      const user = null;
      if (!(user as any)?.payload) {
        const result = null;
        expect(result).toBeNull();
        return;
      }
      expect(false).toBe(true); // Should not reach here
    });

    it('should return undefined for non-existent field', () => {
      mockRequest.user = mockUser;

      // Simulate JwtPayload logic
      const field = 'nonExistentField';
      const user = mockUser;
      if (!(user as any)?.payload) {
        expect(false).toBe(true); // Should not reach here
        return;
      }
      const result = field
        ? user.payload[field as keyof typeof user.payload]
        : user.payload;
      expect(result).toBeUndefined();
    });
  });

  describe('UserUuid', () => {
    it('should return uuid from tokenData when available', () => {
      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: { uuid: '550e8400-e29b-41d4-a716-446655440000' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserUuid logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.uuid || null;
      expect(result).toBe('550e8400-e29b-41d4-a716-446655440000');
    });

    it('should return null when uuid is not in tokenData', () => {
      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' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserUuid logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.uuid || null;
      expect(result).toBeNull();
    });

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

      // Simulate UserUuid logic
      const user = null;
      const result = (user as any)?.tokenData?.uuid || null;
      expect(result).toBeNull();
    });
  });

  describe('UserEmail', () => {
    it('should return email from tokenData when available', () => {
      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: { email: 'user@example.com' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserEmail logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.email || null;
      expect(result).toBe('user@example.com');
    });

    it('should return null when email is not in tokenData', () => {
      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' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserEmail logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.email || null;
      expect(result).toBeNull();
    });

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

      // Simulate UserEmail logic
      const user = null;
      const result = (user as any)?.tokenData?.email || null;
      expect(result).toBeNull();
    });
  });

  describe('UserName', () => {
    it('should return name from tokenData when available', () => {
      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: { name: 'John Doe' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserName logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.name || null;
      expect(result).toBe('John Doe');
    });

    it('should return null when name is not in tokenData', () => {
      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' },
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Simulate UserName logic
      const user = mockUser;
      const result = (user as any)?.tokenData?.name || null;
      expect(result).toBeNull();
    });

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

      // Simulate UserName logic
      const user = null;
      const result = (user as any)?.tokenData?.name || null;
      expect(result).toBeNull();
    });
  });

  describe('Edge cases', () => {
    it('should handle user with empty tokenData object', () => {
      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: {},
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Test TokenData logic
      const field = 'role';
      const user = mockUser;
      if (!(user as any)?.tokenData) {
        expect(false).toBe(true);
        return;
      }
      const tokenDataResult = field ? user.tokenData[field] : user.tokenData;
      expect(tokenDataResult).toBeUndefined();

      // Test UserUuid logic
      const userUuidResult = user?.tokenData?.uuid || null;
      expect(userUuidResult).toBeNull();

      // Test UserEmail logic
      const userEmailResult = user?.tokenData?.email || null;
      expect(userEmailResult).toBeNull();

      // Test UserName logic
      const userNameResult = user?.tokenData?.name || null;
      expect(userNameResult).toBeNull();
    });

    it('should handle user with undefined tokenData', () => {
      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: undefined as any,
        rawToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
      };

      mockRequest.user = mockUser;

      // Test TokenData logic
      const field = 'role';
      const user = mockUser;
      if (!(user as any)?.tokenData) {
        const tokenDataResult = null;
        expect(tokenDataResult).toBeNull();
      }

      // Test UserUuid logic
      const userUuidResult = user?.tokenData?.uuid || null;
      expect(userUuidResult).toBeNull();

      // Test UserEmail logic
      const userEmailResult = user?.tokenData?.email || null;
      expect(userEmailResult).toBeNull();

      // Test UserName logic
      const userNameResult = user?.tokenData?.name || null;
      expect(userNameResult).toBeNull();
    });
  });
});
