import { Injectable } from '@nestjs/common';
import { AuditRepository, AuditFilters, AuditItemContext } from './repositories/audit.repository';
import { AuditChange, diffSnapshots } from './audit-types';

@Injectable()
export class AuditService {
  constructor(private readonly repo: AuditRepository) {}

  async log(entityType: string, entityId: string, action: string, changes?: AuditChange[] | null) {
    return this.repo.log(entityType, entityId, action, changes);
  }

  /**
   * Compute a field-level diff between two snapshots and persist it as
   * an `updated` audit entry. Skips audit-managed columns and redacts
   * secret-bearing fields. If nothing changed, no entry is written.
   */
  async diffAndLog(
    entityType: string,
    entityId: string,
    before: Record<string, unknown> | null | undefined,
    after: Record<string, unknown> | null | undefined,
    action = 'updated',
  ) {
    const changes = diffSnapshots(before, after);
    if (changes.length === 0) return null;
    return this.repo.log(entityType, entityId, action, changes);
  }

  async getRecentActivity(limit = 10) {
    const items = await this.repo.findRecent(limit);
    return items.map((item) => ({
      id: item.id,
      entity_type: item.entity_type,
      entity_id: item.entity_id,
      action: item.action,
      performer_name: (item as { performer?: { name?: string } }).performer?.name || null,
      created_at: item.created_at,
    }));
  }

  async getByEntity(
    entityType: string,
    entityId: string,
    query?: { page?: number; limit?: number },
  ) {
    const result = await this.repo.findByEntity(entityType, entityId, query);
    const context = await this.repo.loadItemContext(result.items);
    return { ...result, context };
  }

  async getFiltered(filters: AuditFilters): Promise<{
    items: Awaited<ReturnType<AuditRepository['findFiltered']>>['items'];
    meta: Awaited<ReturnType<AuditRepository['findFiltered']>>['meta'];
    context: Map<string, AuditItemContext>;
  }> {
    const result = await this.repo.findFiltered(filters);
    const context = await this.repo.loadItemContext(result.items);
    return { ...result, context };
  }
}
