import type { DataSource } from 'typeorm';
import { IMagicLinkRepository, PasswordResetRequest } from '@core/identity/domain/repositories/IMagicLinkRepository';
import { Result } from '@core/shared/application/Result';
import { Logger } from '@core/shared/application';
import { PasswordResetRequestOrmEntity } from '../entities/PasswordResetRequestOrmEntity';

export class TypeOrmMagicLinkRepository implements IMagicLinkRepository {
  // 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 dataSource: DataSource,
    private readonly logger: Logger,
  ) {}

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

    const repo = this.dataSource.getRepository(PasswordResetRequestOrmEntity);
    
    const entity = new PasswordResetRequestOrmEntity();
    entity.email = request.email;
    entity.token = request.token;
    entity.expiresAt = request.expiresAt;
    entity.userId = request.userId;
    entity.used = false;
    entity.attemptCount = 0;

    await repo.save(entity);
  }

  async findByToken(token: string): Promise<PasswordResetRequest | null> {
    const repo = this.dataSource.getRepository(PasswordResetRequestOrmEntity);
    
    const entity = await repo.findOne({ where: { token } });
    
    if (!entity) {
      return null;
    }

    // Check if expired
    if (entity.expiresAt < new Date()) {
      await repo.delete(entity.id);
      return null;
    }

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

    return {
      email: entity.email,
      token: entity.token,
      expiresAt: entity.expiresAt,
      userId: entity.userId,
      used: entity.used,
    };
  }

  async markAsUsed(token: string): Promise<void> {
    const repo = this.dataSource.getRepository(PasswordResetRequestOrmEntity);
    
    await repo.update(
      { token },
      { 
        used: true,
        attemptCount: () => 'attemptCount + 1'
      }
    );

    this.logger.debug('[TypeOrmMagicLinkRepository] Marked token as used', {
      token: token.substring(0, 10) + '...',
    });
  }

  async checkRateLimit(email: string): Promise<Result<void, { message: string }>> {
    const repo = this.dataSource.getRepository(PasswordResetRequestOrmEntity);
    const now = new Date();
    const windowStart = new Date(now.getTime() - this.RATE_LIMIT_WINDOW);

    // Count requests in the current window
    const recentRequests = await repo.count({
      where: {
        email,
        createdAt: windowStart,
        used: false,
      },
    });

    if (recentRequests >= this.RATE_LIMIT_MAX) {
      // Find the oldest request to calculate remaining time
      const oldestRequest = await repo.findOne({
        where: { email },
        order: { createdAt: 'ASC' },
      });

      if (oldestRequest) {
        const timeRemaining = Math.ceil(
          (this.RATE_LIMIT_WINDOW - (now.getTime() - oldestRequest.createdAt.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 repo = this.dataSource.getRepository(PasswordResetRequestOrmEntity);
    const now = new Date();

    const result = await repo.delete({
      expiresAt: now,
    });

    if (result.affected && result.affected > 0) {
      this.logger.debug('[TypeOrmMagicLinkRepository] Cleaned up expired tokens', {
        count: result.affected,
      });
    }
  }
}