import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { AuthPageParams } from '@/lib/services/auth/AuthPageParams';
import { LoginPageDTO } from '@/lib/services/auth/types/LoginPageDTO';
import { ForgotPasswordPageDTO } from '@/lib/services/auth/types/ForgotPasswordPageDTO';
import { ResetPasswordPageDTO } from '@/lib/services/auth/types/ResetPasswordPageDTO';
import { SignupPageDTO } from '@/lib/services/auth/types/SignupPageDTO';

describe('AuthPageService', () => {
  let service: AuthPageService;

  beforeEach(() => {
    service = new AuthPageService();
  });

  describe('processLoginParams', () => {
    describe('happy paths', () => {
      it('should process login params with returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: '/dashboard',
        };

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/dashboard');
        expect(dto.hasInsufficientPermissions).toBe(true);
      });

      it('should process login params with null returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: null,
        };

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/dashboard');
        expect(dto.hasInsufficientPermissions).toBe(false);
      });

      it('should process login params with undefined returnTo', async () => {
        const params: AuthPageParams = {};

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/dashboard');
        expect(dto.hasInsufficientPermissions).toBe(false);
      });

      it('should process login params with empty string returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: '',
        };

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('');
        expect(dto.hasInsufficientPermissions).toBe(true);
      });
    });

    describe('decision branches', () => {
      it('should handle different returnTo paths', async () => {
        const paths = [
          '/dashboard',
          '/settings',
          '/profile',
          '/admin',
          '/projects/123',
          '/projects/123/tasks',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processLoginParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
          expect(dto.hasInsufficientPermissions).toBe(true);
        }
      });

      it('should handle special characters in returnTo path', async () => {
        const paths = [
          '/dashboard?param=value',
          '/dashboard#section',
          '/dashboard/with/slashes',
          '/dashboard/with-dashes',
          '/dashboard/with_underscores',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processLoginParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
          expect(dto.hasInsufficientPermissions).toBe(true);
        }
      });

      it('should handle different returnTo values and hasInsufficientPermissions', async () => {
        const testCases = [
          { returnTo: '/dashboard', expectedHasInsufficientPermissions: true },
          { returnTo: null, expectedHasInsufficientPermissions: false },
          { returnTo: undefined, expectedHasInsufficientPermissions: false },
          { returnTo: '', expectedHasInsufficientPermissions: true },
        ];

        for (const testCase of testCases) {
          const params: AuthPageParams = {
            returnTo: testCase.returnTo as string | null | undefined,
          };

          const result = await service.processLoginParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.hasInsufficientPermissions).toBe(testCase.expectedHasInsufficientPermissions);
        }
      });
    });

    describe('aggregation logic', () => {
      it('should aggregate login params into DTO correctly', async () => {
        const params: AuthPageParams = {
          returnTo: '/dashboard',
        };

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify all fields are correctly aggregated
        expect(dto.returnTo).toBe('/dashboard');
        expect(dto.hasInsufficientPermissions).toBe(true);
        expect(typeof dto.returnTo).toBe('string');
        expect(typeof dto.hasInsufficientPermissions).toBe('boolean');
      });

      it('should handle empty params object', async () => {
        const params: AuthPageParams = {};

        const result = await service.processLoginParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify default values are used
        expect(dto.returnTo).toBe('/dashboard');
        expect(dto.hasInsufficientPermissions).toBe(false);
      });
    });
  });

  describe('processForgotPasswordParams', () => {
    describe('happy paths', () => {
      it('should process forgot password params with returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: '/auth/login',
        };

        const result = await service.processForgotPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/auth/login');
      });

      it('should process forgot password params with null returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: null,
        };

        const result = await service.processForgotPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/auth/login');
      });

      it('should process forgot password params with undefined returnTo', async () => {
        const params: AuthPageParams = {};

        const result = await service.processForgotPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/auth/login');
      });
    });

    describe('decision branches', () => {
      it('should handle different returnTo paths', async () => {
        const paths = [
          '/auth/login',
          '/auth/signup',
          '/dashboard',
          '/settings',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processForgotPasswordParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });

      it('should handle special characters in returnTo path', async () => {
        const paths = [
          '/auth/login?param=value',
          '/auth/login#section',
          '/auth/login/with/slashes',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processForgotPasswordParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });
    });

    describe('aggregation logic', () => {
      it('should aggregate forgot password params into DTO correctly', async () => {
        const params: AuthPageParams = {
          returnTo: '/auth/login',
        };

        const result = await service.processForgotPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify all fields are correctly aggregated
        expect(dto.returnTo).toBe('/auth/login');
        expect(typeof dto.returnTo).toBe('string');
      });

      it('should handle empty params object', async () => {
        const params: AuthPageParams = {};

        const result = await service.processForgotPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify default values are used
        expect(dto.returnTo).toBe('/auth/login');
      });
    });
  });

  describe('processResetPasswordParams', () => {
    describe('happy paths', () => {
      it('should process reset password params with token and returnTo', async () => {
        const params: AuthPageParams = {
          token: 'reset-token-123',
          returnTo: '/auth/login',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.token).toBe('reset-token-123');
        expect(dto.returnTo).toBe('/auth/login');
      });

      it('should process reset password params with token and null returnTo', async () => {
        const params: AuthPageParams = {
          token: 'reset-token-123',
          returnTo: null,
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.token).toBe('reset-token-123');
        expect(dto.returnTo).toBe('/auth/login');
      });

      it('should process reset password params with token and undefined returnTo', async () => {
        const params: AuthPageParams = {
          token: 'reset-token-123',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.token).toBe('reset-token-123');
        expect(dto.returnTo).toBe('/auth/login');
      });
    });

    describe('failure modes', () => {
      it('should return error when token is missing', async () => {
        const params: AuthPageParams = {
          returnTo: '/auth/login',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isErr()).toBe(true);
        expect(result.getError().type).toBe('validation');
        expect(result.getError().message).toBe('Missing reset token');
      });

      it('should return error when token is null', async () => {
        const params: AuthPageParams = {
          token: null,
          returnTo: '/auth/login',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isErr()).toBe(true);
        expect(result.getError().type).toBe('validation');
        expect(result.getError().message).toBe('Missing reset token');
      });

      it('should return error when token is empty string', async () => {
        const params: AuthPageParams = {
          token: '',
          returnTo: '/auth/login',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isErr()).toBe(true);
        expect(result.getError().type).toBe('validation');
        expect(result.getError().message).toBe('Missing reset token');
      });
    });

    describe('decision branches', () => {
      it('should handle different token formats', async () => {
        const tokens = [
          'reset-token-123',
          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
          'token-with-special-chars-!@#$%^&*()',
        ];

        for (const token of tokens) {
          const params: AuthPageParams = {
            token,
            returnTo: '/auth/login',
          };

          const result = await service.processResetPasswordParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.token).toBe(token);
        }
      });

      it('should handle different returnTo paths', async () => {
        const paths = [
          '/auth/login',
          '/auth/signup',
          '/dashboard',
          '/settings',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            token: 'reset-token-123',
            returnTo: path,
          };

          const result = await service.processResetPasswordParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });

      it('should handle special characters in returnTo path', async () => {
        const paths = [
          '/auth/login?param=value',
          '/auth/login#section',
          '/auth/login/with/slashes',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            token: 'reset-token-123',
            returnTo: path,
          };

          const result = await service.processResetPasswordParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });
    });

    describe('aggregation logic', () => {
      it('should aggregate reset password params into DTO correctly', async () => {
        const params: AuthPageParams = {
          token: 'reset-token-123',
          returnTo: '/auth/login',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify all fields are correctly aggregated
        expect(dto.token).toBe('reset-token-123');
        expect(dto.returnTo).toBe('/auth/login');
        expect(typeof dto.token).toBe('string');
        expect(typeof dto.returnTo).toBe('string');
      });

      it('should handle params with only token', async () => {
        const params: AuthPageParams = {
          token: 'reset-token-123',
        };

        const result = await service.processResetPasswordParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify default returnTo is used
        expect(dto.token).toBe('reset-token-123');
        expect(dto.returnTo).toBe('/auth/login');
      });
    });
  });

  describe('processSignupParams', () => {
    describe('happy paths', () => {
      it('should process signup params with returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: '/onboarding',
        };

        const result = await service.processSignupParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/onboarding');
      });

      it('should process signup params with null returnTo', async () => {
        const params: AuthPageParams = {
          returnTo: null,
        };

        const result = await service.processSignupParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/onboarding');
      });

      it('should process signup params with undefined returnTo', async () => {
        const params: AuthPageParams = {};

        const result = await service.processSignupParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();
        expect(dto.returnTo).toBe('/onboarding');
      });
    });

    describe('decision branches', () => {
      it('should handle different returnTo paths', async () => {
        const paths = [
          '/onboarding',
          '/dashboard',
          '/settings',
          '/projects',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processSignupParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });

      it('should handle special characters in returnTo path', async () => {
        const paths = [
          '/onboarding?param=value',
          '/onboarding#section',
          '/onboarding/with/slashes',
        ];

        for (const path of paths) {
          const params: AuthPageParams = {
            returnTo: path,
          };

          const result = await service.processSignupParams(params);

          expect(result.isOk()).toBe(true);
          const dto = result.unwrap();
          expect(dto.returnTo).toBe(path);
        }
      });
    });

    describe('aggregation logic', () => {
      it('should aggregate signup params into DTO correctly', async () => {
        const params: AuthPageParams = {
          returnTo: '/onboarding',
        };

        const result = await service.processSignupParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify all fields are correctly aggregated
        expect(dto.returnTo).toBe('/onboarding');
        expect(typeof dto.returnTo).toBe('string');
      });

      it('should handle empty params object', async () => {
        const params: AuthPageParams = {};

        const result = await service.processSignupParams(params);

        expect(result.isOk()).toBe(true);
        const dto = result.unwrap();

        // Verify default values are used
        expect(dto.returnTo).toBe('/onboarding');
      });
    });
  });

  describe('error handling', () => {
    it('should handle unexpected error types in processLoginParams', async () => {
      const params: AuthPageParams = {
        returnTo: '/dashboard',
      };

      // This should not throw an error
      const result = await service.processLoginParams(params);
      expect(result.isOk()).toBe(true);
    });

    it('should handle unexpected error types in processForgotPasswordParams', async () => {
      const params: AuthPageParams = {
        returnTo: '/auth/login',
      };

      // This should not throw an error
      const result = await service.processForgotPasswordParams(params);
      expect(result.isOk()).toBe(true);
    });

    it('should handle unexpected error types in processResetPasswordParams', async () => {
      const params: AuthPageParams = {
        token: 'reset-token-123',
        returnTo: '/auth/login',
      };

      // This should not throw an error
      const result = await service.processResetPasswordParams(params);
      expect(result.isOk()).toBe(true);
    });

    it('should handle unexpected error types in processSignupParams', async () => {
      const params: AuthPageParams = {
        returnTo: '/onboarding',
      };

      // This should not throw an error
      const result = await service.processSignupParams(params);
      expect(result.isOk()).toBe(true);
    });
  });
});
