import { describe, expect, it, vi } from 'vitest';
import { AuthService } from './AuthService';
import { Result } from '@core/shared/application/Result';

class FakeAuthSessionPresenter {
  private model: any = null;
  reset() { this.model = null; }
  present(model: any) { this.model = model; }
  get responseModel() {
    if (!this.model) throw new Error('Presenter not presented');
    return this.model;
  }
}

class FakeCommandResultPresenter {
  private model: any = null;
  reset() { this.model = null; }
  present(model: any) { this.model = model; }
  get responseModel() {
    if (!this.model) throw new Error('Presenter not presented');
    return this.model;
  }
}

class FakeForgotPasswordPresenter {
  private model: any = null;
  reset() { this.model = null; }
  present(model: any) { this.model = model; }
  get responseModel() {
    if (!this.model) throw new Error('Presenter not presented');
    return this.model;
  }
}

class FakeResetPasswordPresenter {
  private model: any = null;
  reset() { this.model = null; }
  present(model: any) { this.model = model; }
  get responseModel() {
    if (!this.model) throw new Error('Presenter not presented');
    return this.model;
  }
}

class FakeDemoLoginPresenter {
  private model: any = null;
  reset() { this.model = null; }
  present(model: any) { this.model = model; }
  get responseModel() {
    if (!this.model) throw new Error('Presenter not presented');
    return this.model;
  }
}

describe('AuthService - New Methods', () => {
  describe('forgotPassword', () => {
    it('should execute forgot password use case and return result', async () => {
      const forgotPasswordPresenter = new FakeForgotPasswordPresenter();
      const forgotPasswordUseCase = {
        execute: vi.fn(async () => {
          forgotPasswordPresenter.present({ message: 'Reset link sent', magicLink: 'http://example.com/reset?token=abc123' });
          return Result.ok(undefined);
        }),
      };

      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        { getCurrentSession: vi.fn(), createSession: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        forgotPasswordPresenter as any,
        new FakeResetPasswordPresenter() as any,
        new FakeDemoLoginPresenter() as any,
      );

      const result = await service.forgotPassword({ email: 'test@example.com' });

      expect(forgotPasswordUseCase.execute).toHaveBeenCalledWith({ email: 'test@example.com' });
      expect(result).toEqual({
        message: 'Reset link sent',
        magicLink: 'http://example.com/reset?token=abc123',
      });
    });

    it('should throw error on use case failure', async () => {
      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        { getCurrentSession: vi.fn(), createSession: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn(async () => Result.err({ code: 'RATE_LIMIT_EXCEEDED', details: { message: 'Too many attempts' } })) } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        new FakeForgotPasswordPresenter() as any,
        new FakeResetPasswordPresenter() as any,
        new FakeDemoLoginPresenter() as any,
      );

      await expect(service.forgotPassword({ email: 'test@example.com' })).rejects.toThrow('Too many attempts');
    });
  });

  describe('resetPassword', () => {
    it('should execute reset password use case and return result', async () => {
      const resetPasswordPresenter = new FakeResetPasswordPresenter();
      const resetPasswordUseCase = {
        execute: vi.fn(async () => {
          resetPasswordPresenter.present({ message: 'Password reset successfully' });
          return Result.ok(undefined);
        }),
      };

      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        { getCurrentSession: vi.fn(), createSession: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        resetPasswordUseCase as any,
        { execute: vi.fn() } as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        new FakeForgotPasswordPresenter() as any,
        resetPasswordPresenter as any,
        new FakeDemoLoginPresenter() as any,
      );

      const result = await service.resetPassword({
        token: 'abc123',
        newPassword: 'NewPass123!',
      });

      expect(resetPasswordUseCase.execute).toHaveBeenCalledWith({
        token: 'abc123',
        newPassword: 'NewPass123!',
      });
      expect(result).toEqual({ message: 'Password reset successfully' });
    });

    it('should throw error on use case failure', async () => {
      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        { getCurrentSession: vi.fn(), createSession: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn(async () => Result.err({ code: 'INVALID_TOKEN', details: { message: 'Invalid token' } })) } as any,
        { execute: vi.fn() } as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        new FakeForgotPasswordPresenter() as any,
        new FakeResetPasswordPresenter() as any,
        new FakeDemoLoginPresenter() as any,
      );

      await expect(
        service.resetPassword({ token: 'invalid', newPassword: 'NewPass123!' })
      ).rejects.toThrow('Invalid token');
    });
  });

  describe('demoLogin', () => {
    it('should execute demo login use case and create session', async () => {
      const demoLoginPresenter = new FakeDemoLoginPresenter();
      const mockUser = {
        getId: () => ({ value: 'demo-user-123' }),
        getDisplayName: () => 'Demo Driver',
        getEmail: () => 'demo.driver@example.com',
      };

      const demoLoginUseCase = {
        execute: vi.fn(async () => {
          demoLoginPresenter.present({ user: mockUser });
          return Result.ok(undefined);
        }),
      };

      const identitySessionPort = {
        getCurrentSession: vi.fn(),
        createSession: vi.fn(async () => ({ token: 'demo-token-123' })),
      };

      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        identitySessionPort as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        demoLoginUseCase as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        new FakeForgotPasswordPresenter() as any,
        new FakeResetPasswordPresenter() as any,
        demoLoginPresenter as any,
      );

      const result = await service.demoLogin({ role: 'driver' });

      expect(demoLoginUseCase.execute).toHaveBeenCalledWith({ role: 'driver' });
      expect(identitySessionPort.createSession).toHaveBeenCalledWith({
        id: 'demo-user-123',
        displayName: 'Demo Driver',
        email: 'demo.driver@example.com',
      });
      expect(result).toEqual({
        token: 'demo-token-123',
        user: {
          userId: 'demo-user-123',
          email: 'demo.driver@example.com',
          displayName: 'Demo Driver',
        },
      });
    });

    it('should throw error on use case failure', async () => {
      const service = new AuthService(
        { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
        { getCurrentSession: vi.fn(), createSession: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn() } as any,
        { execute: vi.fn(async () => Result.err({ code: 'DEMO_NOT_ALLOWED', details: { message: 'Demo not allowed' } })) } as any,
        new FakeAuthSessionPresenter() as any,
        new FakeCommandResultPresenter() as any,
        new FakeForgotPasswordPresenter() as any,
        new FakeResetPasswordPresenter() as any,
        new FakeDemoLoginPresenter() as any,
      );

      await expect(service.demoLogin({ role: 'driver' })).rejects.toThrow('Demo not allowed');
    });
  });
});