import type { MagicLinkRepository, PasswordResetRequest } from '@core/identity/domain/repositories/MagicLinkRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';

export class InMemoryMagicLinkRepository implements MagicLinkRepository {
  private resetRequests: Map<string, PasswordResetRequest> = new Map();
  private rateLimitStore: Map<string, { count: number; lastRequest: Date }> = new Map();
  
  // Rate limit: max 3 requests per 15 minutes
  private readonly RATE_LIMIT_MAX = 3;
  private readonly RATE_LIMIT_WINDOW = 15 * 60 * 1000; // 15 minutes

  constructor(private readonly logger: Logger) {}

  async createPasswordResetRequest(request: PasswordResetRequest): Promise<void> {
    this.logger.debug('[InMemoryMagicLinkRepository] Creating password reset request', {
      email: request.email,
      token: request.token.substring(0, 10) + '...',
      expiresAt: request.expiresAt,
    });

    this.resetRequests.set(request.token, request);

    // Update rate limit tracking
    const now = new Date();
    const existing = this.rateLimitStore.get(request.email);
    
    if (existing) {
      // Reset count if window has passed
      if (now.getTime() - existing.lastRequest.getTime() > this.RATE_LIMIT_WINDOW) {
        this.rateLimitStore.set(request.email, { count: 1, lastRequest: now });
      } else {
        this.rateLimitStore.set(request.email, { 
          count: existing.count + 1, 
          lastRequest: now 
        });
      }
    } else {
      this.rateLimitStore.set(request.email, { count: 1, lastRequest: now });
    }
  }

  async findByToken(token: string): Promise<PasswordResetRequest | null> {
    const request = this.resetRequests.get(token);
    
    if (!request) {
      return null;
    }

    // Check if expired
    if (request.expiresAt < new Date()) {
      this.resetRequests.delete(token);
      return null;
    }

    // Check if already used
    if (request.used) {
      return null;
    }

    return request;
  }

  async markAsUsed(token: string): Promise<void> {
    const request = this.resetRequests.get(token);
    if (request) {
      request.used = true;
      this.logger.debug('[InMemoryMagicLinkRepository] Marked token as used', {
        token: token.substring(0, 10) + '...',
      });
    }
  }

  async checkRateLimit(email: string): Promise<Result<void, { message: string }>> {
    const now = new Date();
    const tracking = this.rateLimitStore.get(email);

    if (!tracking) {
      return Result.ok(undefined);
    }

    // Check if window has passed
    if (now.getTime() - tracking.lastRequest.getTime() > this.RATE_LIMIT_WINDOW) {
      return Result.ok(undefined);
    }

    // Check if exceeded limit
    if (tracking.count >= this.RATE_LIMIT_MAX) {
      const timeRemaining = Math.ceil(
        (this.RATE_LIMIT_WINDOW - (now.getTime() - tracking.lastRequest.getTime())) / 60000
      );
      return Result.err({
        message: `Too many reset attempts. Please try again in ${timeRemaining} minutes.`,
      });
    }

    return Result.ok(undefined);
  }

  async cleanupExpired(): Promise<void> {
    const now = new Date();
    const toDelete: string[] = [];

    for (const [token, request] of this.resetRequests.entries()) {
      if (request.expiresAt < now || request.used) {
        toDelete.push(token);
      }
    }

    toDelete.forEach(token => this.resetRequests.delete(token));

    if (toDelete.length > 0) {
      this.logger.debug('[InMemoryMagicLinkRepository] Cleaned up expired tokens', {
        count: toDelete.length,
      });
    }
  }
}