import { Injectable, Logger, Inject } from '@nestjs/common';
import { AuditLog } from '../../database/models/audit-log.model';
import { AuditAction, ActorType, EntityType } from '../../common/enums';

export interface CreateAuditLogDto {
  entityType: string;
  entityId: string;
  action: string;
  actorType: string;
  actorId?: string;
  oldValue?: Record<string, unknown>;
  newValue?: Record<string, unknown>;
  ipAddress?: string;
  userAgent?: string;
  correlationId?: string;
}

export interface AuditQueryDto {
  entityType?: string;
  entityId?: string;
  action?: string;
  actorType?: string;
  actorId?: string;
  startDate?: string;
  endDate?: string;
  page?: number;
  limit?: number;
}

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

  constructor(
    @Inject(AuditLog)
    private auditLogModel: typeof AuditLog,
  ) {}

  async record(dto: CreateAuditLogDto): Promise<AuditLog> {
    this.logger.debug(`Recording audit: ${dto.action} on ${dto.entityType}/${dto.entityId}`);

    return this.auditLogModel.query().insert({
      entityType: dto.entityType,
      entityId: dto.entityId,
      action: dto.action,
      actorType: dto.actorType,
      actorId: dto.actorId,
      oldValue: dto.oldValue as any,
      newValue: dto.newValue as any,
      ipAddress: dto.ipAddress,
      userAgent: dto.userAgent,
      correlationId: dto.correlationId,
    });
  }

  async findAll(queryDto: AuditQueryDto) {
    const page = queryDto.page || 1;
    const limit = queryDto.limit || 20;
    const offset = (page - 1) * limit;

    const query = this.auditLogModel.query().orderBy('created_at', 'DESC');

    if (queryDto.entityType) {
      query.where({ entityType: queryDto.entityType });
    }
    if (queryDto.entityId) {
      query.where({ entityId: queryDto.entityId });
    }
    if (queryDto.action) {
      query.where({ action: queryDto.action });
    }
    if (queryDto.actorType) {
      query.where({ actorType: queryDto.actorType });
    }
    if (queryDto.actorId) {
      query.where({ actorId: queryDto.actorId });
    }
    if (queryDto.startDate) {
      query.where('createdAt', '>=', queryDto.startDate);
    }
    if (queryDto.endDate) {
      query.where('createdAt', '<=', queryDto.endDate);
    }

    const [results, total] = await Promise.all([
      query.clone().offset(offset).limit(limit),
      query.clone().resultSize(),
    ]);

    return {
      data: results,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async findByEntity(entityType: string, entityId: string): Promise<any[]> {
    const logs = await this.auditLogModel
      .query()
      .where('entity_type', entityType)
      .where('entity_id', entityId)
      .orderBy('created_at', 'DESC');

    // Transform to add performedBy and details fields from newValue
    return logs.map(log => ({
      ...log,
      performedBy: (log.newValue as any)?.performedBy,
      details:
        (log.newValue as any)?.reason ||
        (log.newValue as any)?.remarks ||
        (log.action === 'REQUEST_CANCELLED' ? (log.newValue as any)?.reason : undefined),
    }));
  }

  async getEntityActions(): Promise<{
    actions: string[];
    entityTypes: string[];
    actorTypes: string[];
  }> {
    return {
      actions: Object.values(AuditAction),
      entityTypes: Object.values(EntityType),
      actorTypes: Object.values(ActorType),
    };
  }
}
