import type { Request, Response, NextFunction } from "express";
import { storage } from "./storage";

interface AuditableRequest extends Request {
  user?: any;
  auditLog?: {
    resource: string;
    action: string;
    resourceId?: string;
    description: string;
    beforeState?: any;
  };
}

export function auditLog(config: {
  resource: string;
  action: string;
  getResourceId?: (req: AuditableRequest) => string;
  getDescription: (req: AuditableRequest) => string;
}) {
  return async (req: AuditableRequest, res: Response, next: NextFunction) => {
    // Store audit config for later use in response
    req.auditLog = {
      resource: config.resource,
      action: config.action,
      resourceId: config.getResourceId ? config.getResourceId(req) : undefined,
      description: config.getDescription(req),
    };

    // Capture response for after state
    const originalJson = res.json.bind(res);
    res.json = function(data: any) {
      // Log the audit after successful response
      if (res.statusCode >= 200 && res.statusCode < 300 && req.user && req.auditLog) {
        setImmediate(async () => {
          try {
            await storage.createAuditLog({
              userId: req.user.id,
              organizationId: req.user.organizationId || undefined,
              action: req.auditLog!.action,
              resource: req.auditLog!.resource,
              resourceId: req.auditLog!.resourceId,
              description: req.auditLog!.description,
              ipAddress: req.ip || req.socket.remoteAddress,
              userAgent: req.get('user-agent'),
              changes: req.auditLog!.beforeState ? {
                before: req.auditLog!.beforeState,
                after: data
              } : undefined,
              metadata: {
                method: req.method,
                path: req.path,
                statusCode: res.statusCode
              }
            });
          } catch (error) {
            console.error("Failed to create audit log:", error);
          }
        });
      }
      
      return originalJson(data);
    };

    next();
  };
}

// Convenience middleware for common audit scenarios
export const auditCreate = (resource: string, getDescription: (req: AuditableRequest) => string) =>
  auditLog({ resource, action: "create", getDescription });

export const auditUpdate = (resource: string, getResourceId: (req: AuditableRequest) => string, getDescription: (req: AuditableRequest) => string) =>
  auditLog({ resource, action: "update", getResourceId, getDescription });

export const auditDelete = (resource: string, getResourceId: (req: AuditableRequest) => string, getDescription: (req: AuditableRequest) => string) =>
  auditLog({ resource, action: "delete", getResourceId, getDescription });

export const auditRead = (resource: string, getDescription: (req: AuditableRequest) => string) =>
  auditLog({ resource, action: "read", getDescription });

// Auto-audit middleware for sensitive routes
export function autoAudit(req: AuditableRequest, res: Response, next: NextFunction) {
  if (!req.user) {
    return next();
  }

  // Determine resource from path
  const pathParts = req.path.split('/').filter(Boolean);
  const resource = pathParts[1] || 'unknown'; // e.g., /api/users -> users
  const resourceId = pathParts[2]; // e.g., /api/users/123 -> 123

  // Build description
  const action = req.method.toLowerCase();
  let description = `${action.toUpperCase()} ${resource}`;
  if (resourceId && !isNaN(Number(resourceId))) {
    description += ` #${resourceId}`;
  }

  req.auditLog = {
    resource,
    action,
    resourceId,
    description,
  };

  // Capture response
  const originalJson = res.json.bind(res);
  res.json = function(data: any) {
    // Log the audit after successful response
    if (res.statusCode >= 200 && res.statusCode < 300 && req.user && req.auditLog) {
      setImmediate(async () => {
        try {
          await storage.createAuditLog({
            userId: req.user.id,
            organizationId: req.user.organizationId || undefined,
            action: req.auditLog!.action,
            resource: req.auditLog!.resource,
            resourceId: req.auditLog!.resourceId,
            description: req.auditLog!.description,
            ipAddress: req.ip || req.socket.remoteAddress,
            userAgent: req.get('user-agent'),
            metadata: {
              method: req.method,
              path: req.path,
              statusCode: res.statusCode
            }
          });
        } catch (error) {
          console.error("Failed to create audit log:", error);
        }
      });
    }
    
    return originalJson(data);
  };

  next();
}
