import { Inject, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GetBackupShareQuery } from './get-backup-share.query';
import { BackupShareStatus, BACKUP_SHARE_REPOSITORY } from '../../../domain';
import type { BackupShareRepository } from '../../../domain';
import { AesEncryptionService } from '../../../infrastructure/crypto/aes-encryption.service';
import { AuditLogRepository } from '../../../infrastructure/persistence/repositories/audit-log.repository';
import { ApplicationError } from '../../errors/application.error';

export interface BackupShareDto {
  encryptedShareData: string;
  partyIndex: number;
  publicKey: string;
}

@Injectable()
export class GetBackupShareHandler {
  private readonly logger = new Logger(GetBackupShareHandler.name);
  private readonly maxRetrievesPerDay: number;

  constructor(
    @Inject(BACKUP_SHARE_REPOSITORY)
    private readonly repository: BackupShareRepository,
    private readonly encryptionService: AesEncryptionService,
    private readonly auditLogRepository: AuditLogRepository,
    private readonly configService: ConfigService,
  ) {
    this.maxRetrievesPerDay = this.configService.get<number>('MAX_RETRIEVE_PER_DAY') || 3;
  }

  async execute(query: GetBackupShareQuery): Promise<BackupShareDto> {
    this.logger.log(`Retrieving backup share for user: ${query.userId}`);

    const userId = BigInt(query.userId);

    // Check rate limit
    const retrievesToday = await this.auditLogRepository.countRetrievesByUserToday(userId);
    if (retrievesToday >= this.maxRetrievesPerDay) {
      await this.auditLogRepository.log({
        shareId: 0n,
        userId,
        action: 'RETRIEVE',
        sourceService: query.sourceService,
        sourceIp: query.sourceIp,
        success: false,
        errorMessage: 'Rate limit exceeded',
      });
      throw new ApplicationError(
        `Rate limit exceeded. Maximum ${this.maxRetrievesPerDay} retrieves per day.`,
        'RATE_LIMIT_EXCEEDED',
      );
    }

    // Find share
    const share = await this.repository.findByUserIdAndPublicKey(
      userId,
      query.publicKey,
    );

    if (!share) {
      await this.auditLogRepository.log({
        shareId: 0n,
        userId,
        action: 'RETRIEVE',
        sourceService: query.sourceService,
        sourceIp: query.sourceIp,
        success: false,
        errorMessage: 'Share not found',
      });
      throw new ApplicationError(
        'Backup share not found',
        'SHARE_NOT_FOUND',
      );
    }

    if (share.status !== BackupShareStatus.ACTIVE) {
      await this.auditLogRepository.log({
        shareId: share.shareId!,
        userId,
        action: 'RETRIEVE',
        sourceService: query.sourceService,
        sourceIp: query.sourceIp,
        success: false,
        errorMessage: `Share is ${share.status}`,
      });
      throw new ApplicationError(
        'Backup share is not active',
        'SHARE_NOT_ACTIVE',
      );
    }

    // Record access
    share.recordAccess();
    await this.repository.save(share);

    // Decrypt the data (removes our encryption layer, returns identity-service encrypted data)
    const decrypted = await this.encryptionService.decrypt(
      share.encryptedShareData,
      share.encryptionKeyId,
    );

    // Log audit
    await this.auditLogRepository.log({
      shareId: share.shareId!,
      userId,
      action: 'RETRIEVE',
      sourceService: query.sourceService,
      sourceIp: query.sourceIp,
      success: true,
    });

    this.logger.log(`Backup share retrieved: shareId=${share.shareId}`);

    return {
      encryptedShareData: decrypted,
      partyIndex: share.partyIndex,
      publicKey: share.publicKey,
    };
  }
}
