import { beforeEach, describe, expect, it } from 'vitest';
import { InMemoryMagicLinkRepository } from './InMemoryMagicLinkRepository';

const mockLogger = {
  debug: () => {},
  info: () => {},
  warn: () => {},
  error: () => {},
};

describe('InMemoryMagicLinkRepository', () => {
  let repository: InMemoryMagicLinkRepository;

  beforeEach(() => {
    repository = new InMemoryMagicLinkRepository(mockLogger as any);
  });

  describe('createPasswordResetRequest', () => {
    it('should create a password reset request', async () => {
      const request = {
        email: 'test@example.com',
        token: 'abc123',
        expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        userId: 'user-123',
      };

      await repository.createPasswordResetRequest(request);

      const found = await repository.findByToken('abc123');
      expect(found).toEqual(request);
    });

    it('should enforce rate limiting', async () => {
      const request = {
        email: 'test@example.com',
        token: 'token1',
        expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        userId: 'user-123',
      };

      // Create 3 requests for same email
      await repository.createPasswordResetRequest({ ...request, token: 'token1' });
      await repository.createPasswordResetRequest({ ...request, token: 'token2' });
      await repository.createPasswordResetRequest({ ...request, token: 'token3' });

      // 4th should fail
      const result = await repository.checkRateLimit('test@example.com');
      expect(result.isErr()).toBe(true);
    });

    it('should allow requests after time window expires', async () => {
      const now = Date.now();
      const request = {
        email: 'test@example.com',
        token: 'token1',
        expiresAt: new Date(now + 15 * 60 * 1000),
        userId: 'user-123',
      };

      // Mock Date.now to return time after rate limit window
      const originalNow = Date.now;
      Date.now = () => now + (16 * 60 * 1000); // 16 minutes later

      try {
        await repository.createPasswordResetRequest(request);
        const found = await repository.findByToken('token1');
        expect(found).toBeDefined();
      } finally {
        Date.now = originalNow;
      }
    });
  });

  describe('findByToken', () => {
    it('should find existing token', async () => {
      const request = {
        email: 'test@example.com',
        token: 'abc123',
        expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        userId: 'user-123',
      };

      await repository.createPasswordResetRequest(request);
      const found = await repository.findByToken('abc123');

      expect(found).toEqual(request);
    });

    it('should return null for non-existent token', async () => {
      const found = await repository.findByToken('nonexistent');
      expect(found).toBeNull();
    });
  });

  describe('markAsUsed', () => {
    it('should mark token as used', async () => {
      const request = {
        email: 'test@example.com',
        token: 'abc123',
        expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        userId: 'user-123',
      };

      await repository.createPasswordResetRequest(request);
      await repository.markAsUsed('abc123');

      const found = await repository.findByToken('abc123');
      expect(found).toBeNull();
    });

    it('should handle non-existent token gracefully', async () => {
      await expect(repository.markAsUsed('nonexistent')).resolves.not.toThrow();
    });
  });

  describe('checkRateLimit', () => {
    it('should allow requests under limit', async () => {
      const result = await repository.checkRateLimit('test@example.com');
      expect(result.isOk()).toBe(true);
    });

    it('should reject requests over limit', async () => {
      const email = 'test@example.com';
      const request = {
        email,
        token: 'token',
        expiresAt: new Date(Date.now() + 15 * 60 * 1000),
        userId: 'user-123',
      };

      // Create 3 requests
      await repository.createPasswordResetRequest({ ...request, token: 'token1' });
      await repository.createPasswordResetRequest({ ...request, token: 'token2' });
      await repository.createPasswordResetRequest({ ...request, token: 'token3' });

      const result = await repository.checkRateLimit(email);
      expect(result.isErr()).toBe(true);
    });
  });
});