// Admin Audit Service
// Audit logging for all admin console actions (DBIS and external admin services)

import { v4 as uuidv4 } from 'uuid';
import prisma from '@/shared/database/prisma';
import { logger } from '@/infrastructure/monitoring/logger';
import { AdminPermission } from './permissions.constants';

export interface AdminActionAudit {
  employeeId: string;
  action: string;
  permission: AdminPermission | string;
  resourceType: string;
  resourceId?: string;
  beforeState?: Record<string, unknown>;
  afterState?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
  ipAddress?: string;
  userAgent?: string;
  /** Project or service name (e.g. orchestration_portal, token_aggregation) for cross-service audit */
  project?: string;
  service?: string;
  /** Outcome: success | failure | partial */
  outcome?: string;
}

export interface AdminAuditEntry {
  id: string;
  employeeId: string;
  action: string;
  permission: string;
  resourceType: string;
  resourceId?: string;
  project?: string;
  service?: string;
  outcome?: string;
  timestamp: Date;
  ipAddress?: string;
  userAgent?: string;
  details?: Record<string, unknown>;
}

export class AdminAuditService {
  /**
   * Log admin action and persist to audit_logs
   */
  async logAction(audit: AdminActionAudit): Promise<void> {
    const id = uuidv4();
    try {
      const details: Record<string, unknown> = {
        permission: audit.permission,
        beforeState: audit.beforeState,
        afterState: audit.afterState,
        metadata: audit.metadata,
        project: audit.project,
        service: audit.service,
        outcome: audit.outcome ?? 'success',
      };

      logger.info('Admin action', {
        auditId: id,
        employeeId: audit.employeeId,
        action: audit.action,
        resourceType: audit.resourceType,
        resourceId: audit.resourceId,
        project: audit.project,
        service: audit.service,
        timestamp: new Date(),
      });

      await prisma.audit_logs.create({
        data: {
          id,
          eventType: 'admin_action',
          entityType: audit.resourceType,
          entityId: audit.resourceId ?? audit.action,
          action: audit.action,
          actorId: audit.employeeId,
          actorType: audit.service ? `service:${audit.service}` : 'employee',
          details,
          timestamp: new Date(),
          ipAddress: audit.ipAddress ?? null,
          userAgent: audit.userAgent ?? null,
        },
      });
    } catch (error) {
      logger.error('Error logging admin action', {
        error: error instanceof Error ? error.message : 'Unknown error',
        auditId: id,
        audit,
      });
    }
  }

  /**
   * Get audit log (admin actions only)
   */
  async getAuditLog(
    employeeId?: string,
    resourceType?: string,
    project?: string,
    service?: string,
    limit: number = 100
  ): Promise<AdminAuditEntry[]> {
    try {
      const where: Record<string, unknown> = { eventType: 'admin_action' };
      if (employeeId) where.actorId = employeeId;
      if (resourceType) where.entityType = resourceType;

      const rows = await prisma.audit_logs.findMany({
        where,
        orderBy: { timestamp: 'desc' },
        take: limit,
      });

      return rows
        .filter((r) => {
          if (project != null || service != null) {
            const d = r.details as Record<string, unknown> | null;
            if (project != null && d?.project !== project) return false;
            if (service != null && d?.service !== service) return false;
          }
          return true;
        })
        .map((r) => ({
          id: r.id,
          employeeId: r.actorId ?? '',
          action: r.action,
          permission: (r.details as Record<string, unknown>)?.permission as string ?? '',
          resourceType: r.entityType,
          resourceId: r.entityId !== r.action ? r.entityId : undefined,
          project: (r.details as Record<string, unknown>)?.project as string | undefined,
          service: (r.details as Record<string, unknown>)?.service as string | undefined,
          outcome: (r.details as Record<string, unknown>)?.outcome as string | undefined,
          timestamp: r.timestamp,
          ipAddress: r.ipAddress ?? undefined,
          userAgent: r.userAgent ?? undefined,
          details: r.details as Record<string, unknown> | undefined,
        }));
    } catch (error) {
      logger.error('Error getting audit log', {
        error: error instanceof Error ? error.message : 'Unknown error',
        employeeId,
        resourceType,
      });
      return [];
    }
  }

  /**
   * Export audit log for regulators
   */
  async exportAuditLog(
    startDate: Date,
    endDate: Date,
    employeeId?: string
  ): Promise<AdminAuditEntry[]> {
    try {
      const where: Record<string, unknown> = {
        eventType: 'admin_action',
        timestamp: { gte: startDate, lte: endDate },
      };
      if (employeeId) where.actorId = employeeId;

      const rows = await prisma.audit_logs.findMany({
        where,
        orderBy: { timestamp: 'asc' },
      });

      return rows.map((r) => ({
        id: r.id,
        employeeId: r.actorId ?? '',
        action: r.action,
        permission: (r.details as Record<string, unknown>)?.permission as string ?? '',
        resourceType: r.entityType,
        resourceId: r.entityId !== r.action ? r.entityId : undefined,
        project: (r.details as Record<string, unknown>)?.project as string | undefined,
        service: (r.details as Record<string, unknown>)?.service as string | undefined,
        outcome: (r.details as Record<string, unknown>)?.outcome as string | undefined,
        timestamp: r.timestamp,
        ipAddress: r.ipAddress ?? undefined,
        userAgent: r.userAgent ?? undefined,
        details: r.details as Record<string, unknown> | undefined,
      }));
    } catch (error) {
      logger.error('Error exporting audit log', {
        error: error instanceof Error ? error.message : 'Unknown error',
        startDate,
        endDate,
        employeeId,
      });
      return [];
    }
  }
}

export const adminAuditService = new AdminAuditService();

