import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AuditLog, AuditAction, EntityType } from '../../database/entities';

export interface AuditLogData {
  userId?: string;
  action: AuditAction;
  entityType: EntityType;
  entityId?: string;
  changes?: {
    before?: Record<string, any>;
    after?: Record<string, any>;
  };
  ipAddress?: string;
  userAgent?: string;
}

@Injectable()
export class AuditService {
  private readonly logger = new Logger(AuditService.name);

  constructor(
    @InjectRepository(AuditLog)
    private auditLogRepository: Repository<AuditLog>,
  ) {}

  /**
   * Log an audit event
   */
  async log(data: AuditLogData): Promise<void> {
    try {
      const auditLog = this.auditLogRepository.create({
        userId: data.userId || null,
        action: data.action,
        entityType: data.entityType,
        entityId: data.entityId || null,
        changes: data.changes || null,
        ipAddress: data.ipAddress || null,
        userAgent: data.userAgent || null,
      });

      await this.auditLogRepository.save(auditLog);

      this.logger.debug(
        `Audit log created: ${data.action} on ${data.entityType}${data.entityId ? ` (${data.entityId})` : ''} by user ${data.userId || 'system'}`,
      );
    } catch (error) {
      // Audit logging should never break the main flow
      this.logger.error('Failed to create audit log', error);
    }
  }

  /**
   * Log a CREATE action
   */
  async logCreate(
    entityType: EntityType,
    entityId: string,
    data: Record<string, any>,
    userId?: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.CREATE,
      entityType,
      entityId,
      changes: { after: data },
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log a READ action (for sensitive data)
   */
  async logRead(
    entityType: EntityType,
    entityId: string,
    userId?: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.READ,
      entityType,
      entityId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log an UPDATE action
   */
  async logUpdate(
    entityType: EntityType,
    entityId: string,
    before: Record<string, any>,
    after: Record<string, any>,
    userId?: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.UPDATE,
      entityType,
      entityId,
      changes: { before, after },
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log a DELETE action
   */
  async logDelete(
    entityType: EntityType,
    entityId: string,
    data: Record<string, any>,
    userId?: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.DELETE,
      entityType,
      entityId,
      changes: { before: data },
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log an EXPORT action (GDPR data export)
   */
  async logExport(
    entityType: EntityType,
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.EXPORT,
      entityType,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log a LOGIN action
   */
  async logLogin(
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.LOGIN,
      entityType: EntityType.USER,
      entityId: userId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log a LOGOUT action
   */
  async logLogout(
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.LOGOUT,
      entityType: EntityType.USER,
      entityId: userId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log consent granted (COPPA)
   */
  async logConsentGranted(
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.CONSENT_GRANTED,
      entityType: EntityType.USER,
      entityId: userId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log consent revoked
   */
  async logConsentRevoked(
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.CONSENT_REVOKED,
      entityType: EntityType.USER,
      entityId: userId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Log data deletion request (GDPR)
   */
  async logDataDeletionRequest(
    userId: string,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.DATA_DELETION_REQUESTED,
      entityType: EntityType.USER,
      entityId: userId,
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }

  /**
   * Get audit logs for a user (GDPR data access)
   */
  async getUserAuditLogs(
    userId: string,
    limit: number = 100,
  ): Promise<AuditLog[]> {
    return this.auditLogRepository.find({
      where: { userId },
      order: { createdAt: 'DESC' },
      take: limit,
    });
  }

  /**
   * Get audit logs for an entity
   */
  async getEntityAuditLogs(
    entityType: EntityType,
    entityId: string,
    limit: number = 50,
  ): Promise<AuditLog[]> {
    return this.auditLogRepository.find({
      where: { entityType, entityId },
      order: { createdAt: 'DESC' },
      take: limit,
    });
  }

  /**
   * Log a security violation (prompt injection, unauthorized access, etc.)
   */
  async logSecurityViolation(
    userId: string,
    violationType: string,
    details: Record<string, any>,
    metadata?: { ipAddress?: string; userAgent?: string },
  ): Promise<void> {
    await this.log({
      userId,
      action: AuditAction.SECURITY_VIOLATION,
      entityType: EntityType.USER,
      entityId: userId,
      changes: {
        after: {
          violationType,
          ...details,
        },
      },
      ipAddress: metadata?.ipAddress,
      userAgent: metadata?.userAgent,
    });
  }
}
