import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between, LessThan } from 'typeorm';
import { Cron, CronExpression } from '@nestjs/schedule';

import { SecurityIpLog } from './security-ip-log.entity';

export interface LogIpData {
  ipAddress: string;
  sessionId: string;
  userId?: string;
  requestPath: string;
  requestMethod: string;
  userAgent?: string;
}

export interface DateRange {
  start: Date;
  end: Date;
}

/**
 * SecurityIpLogService - Manages raw IP logging for law enforcement requests
 *
 * This service is SEPARATE from consent-free analytics:
 * - Analytics: ephemeral session IDs, no device storage, no consent needed
 * - Security logs: raw IP storage with 90-day retention for legal compliance
 *
 * All query methods should be called from admin-only endpoints
 * with mandatory audit logging.
 */
@Injectable()
export class SecurityIpLogService {
  private readonly logger = new Logger(SecurityIpLogService.name);

  constructor(
    @InjectRepository(SecurityIpLog)
    private readonly repo: Repository<SecurityIpLog>,
  ) {}

  /**
   * Log a request with raw IP address.
   *
   * Fire-and-forget pattern: failures are logged but don't block requests.
   * This ensures security logging never impacts user experience.
   */
  async log(data: LogIpData): Promise<void> {
    try {
      await this.repo.insert({
        ipAddress: data.ipAddress,
        sessionId: data.sessionId,
        userId: data.userId ?? null,
        requestPath: data.requestPath,
        requestMethod: data.requestMethod,
        userAgent: data.userAgent ?? null,
        timestamp: new Date(),
      });
    } catch (error) {
      // Log failure but don't throw - security logging should never break user flow
      this.logger.error('Failed to log security IP entry', {
        error: error instanceof Error ? error.message : 'Unknown error',
        sessionId: data.sessionId,
      });
    }
  }

  /**
   * Find all logs for a specific IP address within a date range.
   *
   * RESTRICTED: Must be called from admin-only endpoints with audit logging.
   */
  async findByIp(ipAddress: string, dateRange: DateRange): Promise<SecurityIpLog[]> {
    return this.repo.find({
      where: {
        ipAddress,
        timestamp: Between(dateRange.start, dateRange.end),
      },
      order: { timestamp: 'DESC' },
    });
  }

  /**
   * Find all logs for a specific session ID.
   *
   * RESTRICTED: Must be called from admin-only endpoints with audit logging.
   */
  async findBySessionId(sessionId: string): Promise<SecurityIpLog[]> {
    return this.repo.find({
      where: { sessionId },
      order: { timestamp: 'DESC' },
    });
  }

  /**
   * Find all logs for a specific user ID.
   *
   * RESTRICTED: Must be called from admin-only endpoints with audit logging.
   */
  async findByUserId(userId: string, dateRange?: DateRange): Promise<SecurityIpLog[]> {
    const where: Record<string, unknown> = { userId };

    if (dateRange) {
      where.timestamp = Between(dateRange.start, dateRange.end);
    }

    return this.repo.find({
      where,
      order: { timestamp: 'DESC' },
    });
  }

  /**
   * Automatic data retention: purge logs older than 90 days.
   *
   * Runs daily at 3 AM to minimize database impact.
   * 90 days aligns with typical law enforcement request windows
   * while respecting GDPR storage limitation principles.
   */
  @Cron(CronExpression.EVERY_DAY_AT_3AM)
  async purgeOldLogs(): Promise<void> {
    const retentionDays = 90;
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - retentionDays);

    try {
      const result = await this.repo.delete({
        timestamp: LessThan(cutoff),
      });

      const deletedCount = result.affected ?? 0;
      if (deletedCount > 0) {
        this.logger.log(`Purged ${deletedCount} security IP logs older than ${retentionDays} days`);
      }
    } catch (error) {
      this.logger.error('Failed to purge old security IP logs', {
        error: error instanceof Error ? error.message : 'Unknown error',
        cutoffDate: cutoff.toISOString(),
      });
    }
  }

  /**
   * Get log count for monitoring.
   *
   * Useful for dashboard metrics and capacity planning.
   */
  async getLogCount(dateRange?: DateRange): Promise<number> {
    const where: Record<string, unknown> = {};

    if (dateRange) {
      where.timestamp = Between(dateRange.start, dateRange.end);
    }

    return this.repo.count({ where });
  }
}
