import type * as interfaces from '../../ts_interfaces/index.ts';

export interface IAuditLogEntry {
  timestamp: number;
  actorUserId: string;
  action: string;
  targetType: string;
  targetId?: string;
  success: boolean;
  message?: string;
  metadata?: Record<string, string | number | boolean>;
}

export class AuditLogger {
  constructor(private storageDirectory: string) {}

  public get auditLogPath(): string {
    return `${this.storageDirectory}/.objectstorage/audit.log`;
  }

  public async log(entry: Omit<IAuditLogEntry, 'timestamp'>): Promise<void> {
    const logEntry: IAuditLogEntry = {
      timestamp: Date.now(),
      ...entry,
    };
    const dirPath = this.auditLogPath.substring(0, this.auditLogPath.lastIndexOf('/'));
    await Deno.mkdir(dirPath, { recursive: true });
    await Deno.writeTextFile(this.auditLogPath, `${JSON.stringify(logEntry)}\n`, {
      append: true,
      create: true,
      mode: 0o600,
    });
    await this.restrictAuditLogPermissions();
  }

  public async listRecent(limit = 100): Promise<interfaces.data.IAuditEntry[]> {
    let content = '';
    try {
      content = await Deno.readTextFile(this.auditLogPath);
    } catch (error) {
      if (error instanceof Deno.errors.NotFound) {
        return [];
      }
      throw error;
    }

    return content
      .trim()
      .split('\n')
      .filter(Boolean)
      .slice(-limit)
      .map((line) => JSON.parse(line) as interfaces.data.IAuditEntry)
      .reverse();
  }

  private async restrictAuditLogPermissions(): Promise<void> {
    try {
      await Deno.chmod(this.auditLogPath, 0o600);
    } catch {
      // chmod is not available on every platform Deno supports.
    }
  }
}
