/**
 * Verbose Audit Logger
 * Logs every system action (automated or user-induced) with full context
 */

import { prisma } from '@/lib/db';

export type AuditAction = 
  | 'create' | 'read' | 'update' | 'delete'
  | 'login' | 'logout' | 'auth_failure'
  | 'task_start' | 'task_complete' | 'task_fail'
  | 'message_send' | 'message_read'
  | 'payment_process' | 'invoice_generate'
  | 'dunning_send' | 'reconciliation_run'
  | 'config_change' | 'permission_grant' | 'permission_revoke'
  | 'export' | 'import' | 'api_call'
  | 'system_event' | 'error' | 'warning'
  | 'backup_create' | 'backup_restore' | 'backup_delete';

export type AuditResource = 
  | 'user' | 'role' | 'permission'
  | 'task' | 'task_run' | 'task_subscription'
  | 'message' | 'conversation'
  | 'employee' | 'payroll' | 'bank_account'
  | 'order' | 'invoice' | 'transaction' | 'ar'
  | 'product' | 'inventory'
  | 'customer' | 'vendor'
  | 'document' | 'contract'
  | 'config' | 'system' | 'backup';

export interface AuditContext {
  userId?: string;
  agentId?: string;
  sessionId?: string;
  ipAddress?: string;
  userAgent?: string;
  requestId?: string;
}

export interface AuditDetails {
  action: AuditAction;
  resource: AuditResource;
  resourceId?: string;
  description?: string;
  previousState?: Record<string, any>;
  newState?: Record<string, any>;
  metadata?: Record<string, any>;
  success?: boolean;
  errorMessage?: string;
  executionTimeMs?: number;
}

class AuditLogger {
  private context: AuditContext = {};

  /**
   * Set the context for subsequent log entries
   */
  setContext(context: AuditContext): this {
    this.context = { ...this.context, ...context };
    return this;
  }

  /**
   * Clear the current context
   */
  clearContext(): this {
    this.context = {};
    return this;
  }

  /**
   * Log an audit entry
   */
  async log(details: AuditDetails): Promise<string> {
    const entry = await prisma.auditLog.create({
      data: {
        userId: this.context.userId,
        agentId: this.context.agentId,
        sessionId: this.context.sessionId,
        ipAddress: this.context.ipAddress,
        userAgent: this.context.userAgent,
        action: details.action,
        resource: details.resource,
        resourceId: details.resourceId,
        details: {
          description: details.description,
          requestId: this.context.requestId,
          executionTimeMs: details.executionTimeMs,
          ...details.metadata,
        },
        previousState: details.previousState,
        newState: details.newState,
        success: details.success ?? true,
        errorMessage: details.errorMessage,
      },
    });
    return entry.id;
  }

  /**
   * Log a CRUD operation
   */
  async logCrud(
    action: 'create' | 'read' | 'update' | 'delete',
    resource: AuditResource,
    resourceId: string,
    options?: {
      previousState?: Record<string, any>;
      newState?: Record<string, any>;
      description?: string;
    }
  ): Promise<string> {
    return this.log({
      action,
      resource,
      resourceId,
      previousState: options?.previousState,
      newState: options?.newState,
      description: options?.description || `${action} ${resource} ${resourceId}`,
    });
  }

  /**
   * Log a task execution
   */
  async logTaskExecution(
    taskKey: string,
    runId: string,
    status: 'start' | 'complete' | 'fail',
    details?: {
      executionTimeMs?: number;
      itemsProcessed?: number;
      error?: string;
      output?: Record<string, any>;
    }
  ): Promise<string> {
    const action = status === 'start' ? 'task_start' 
      : status === 'complete' ? 'task_complete' 
      : 'task_fail';

    return this.log({
      action,
      resource: 'task_run',
      resourceId: runId,
      description: `Task ${taskKey} ${status}`,
      success: status !== 'fail',
      errorMessage: details?.error,
      executionTimeMs: details?.executionTimeMs,
      metadata: {
        taskKey,
        itemsProcessed: details?.itemsProcessed,
        output: details?.output,
      },
    });
  }

  /**
   * Log an authentication event
   */
  async logAuth(
    action: 'login' | 'logout' | 'auth_failure',
    userId: string,
    details?: {
      method?: string;
      reason?: string;
    }
  ): Promise<string> {
    return this.log({
      action,
      resource: 'user',
      resourceId: userId,
      description: `User ${action}`,
      success: action !== 'auth_failure',
      errorMessage: details?.reason,
      metadata: { method: details?.method },
    });
  }

  /**
   * Log an error
   */
  async logError(
    resource: AuditResource,
    resourceId: string | undefined,
    error: Error | string,
    metadata?: Record<string, any>
  ): Promise<string> {
    const errorMessage = error instanceof Error ? error.message : error;
    return this.log({
      action: 'error',
      resource,
      resourceId,
      description: errorMessage,
      success: false,
      errorMessage,
      metadata,
    });
  }

  /**
   * Log a system event
   */
  async logSystemEvent(
    event: string,
    details?: Record<string, any>
  ): Promise<string> {
    return this.log({
      action: 'system_event',
      resource: 'system',
      description: event,
      metadata: details,
    });
  }

  /**
   * Log a config change
   */
  async logConfigChange(
    resource: AuditResource,
    resourceId: string,
    previousState: Record<string, any>,
    newState: Record<string, any>,
    description?: string
  ): Promise<string> {
    return this.log({
      action: 'config_change',
      resource,
      resourceId,
      previousState,
      newState,
      description: description || `Config changed for ${resource} ${resourceId}`,
    });
  }

  /**
   * Query audit logs
   */
  async query(params: {
    userId?: string;
    agentId?: string;
    action?: string;
    resource?: string;
    resourceId?: string;
    startDate?: Date;
    endDate?: Date;
    success?: boolean;
    limit?: number;
    offset?: number;
  }) {
    const where: any = {};
    
    if (params.userId) where.userId = params.userId;
    if (params.agentId) where.agentId = params.agentId;
    if (params.action) where.action = params.action;
    if (params.resource) where.resource = params.resource;
    if (params.resourceId) where.resourceId = params.resourceId;
    if (params.success !== undefined) where.success = params.success;
    
    if (params.startDate || params.endDate) {
      where.timestamp = {};
      if (params.startDate) where.timestamp.gte = params.startDate;
      if (params.endDate) where.timestamp.lte = params.endDate;
    }

    const [logs, total] = await Promise.all([
      prisma.auditLog.findMany({
        where,
        orderBy: { timestamp: 'desc' },
        take: params.limit || 100,
        skip: params.offset || 0,
        include: { user: { select: { id: true, name: true, email: true } } },
      }),
      prisma.auditLog.count({ where }),
    ]);

    return { logs, total };
  }
}

// Export singleton instance
export const auditLogger = new AuditLogger();

// Helper to create a scoped logger with context
export function createScopedLogger(context: AuditContext): AuditLogger {
  const logger = new AuditLogger();
  logger.setContext(context);
  return logger;
}
