/**
 * Logging Middleware for API Routes
 * Agent 4: Production Deployment
 *
 * Provides request/response logging middleware for Next.js API routes.
 */

import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next';
import { createLogger, Logger } from './logger';

interface RequestLogContext {
  requestId: string;
  method: string;
  path: string;
  query?: Record<string, string | string[]>;
  userAgent?: string;
  ip?: string;
}

interface ResponseLogContext extends RequestLogContext {
  statusCode: number;
  duration: number;
}

/**
 * Generate a unique request ID
 */
function generateRequestId(): string {
  return `req_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;
}

/**
 * Get client IP from request headers
 */
function getClientIp(req: NextApiRequest): string {
  const forwarded = req.headers['x-forwarded-for'];
  if (typeof forwarded === 'string') {
    return forwarded.split(',')[0].trim();
  }
  if (Array.isArray(forwarded)) {
    return forwarded[0];
  }
  return req.socket?.remoteAddress || 'unknown';
}

/**
 * Sanitize headers for logging (remove sensitive data)
 */
function sanitizeHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string> {
  const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token'];
  const sanitized: Record<string, string> = {};

  for (const [key, value] of Object.entries(headers)) {
    if (sensitiveHeaders.includes(key.toLowerCase())) {
      sanitized[key] = '[REDACTED]';
    } else if (typeof value === 'string') {
      sanitized[key] = value;
    } else if (Array.isArray(value)) {
      sanitized[key] = value.join(', ');
    }
  }

  return sanitized;
}

/**
 * Request logging middleware
 */
export function withLogging(handler: NextApiHandler): NextApiHandler {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    const startTime = Date.now();
    const requestId = generateRequestId();
    const logger = createLogger({ requestId });

    // Extract request information
    const requestContext: RequestLogContext = {
      requestId,
      method: req.method || 'UNKNOWN',
      path: req.url || '/',
      query: req.query as Record<string, string | string[]>,
      userAgent: req.headers['user-agent'],
      ip: getClientIp(req),
    };

    // Log incoming request
    logger.info('Incoming request', {
      ...requestContext,
      headers: sanitizeHeaders(req.headers as Record<string, string | string[] | undefined>),
    });

    // Add request ID to response headers
    res.setHeader('X-Request-Id', requestId);

    // Capture the original end method
    const originalEnd = res.end;
    let responseLogged = false;

    // Override end to log response
    res.end = function (this: NextApiResponse, ...args: Parameters<typeof originalEnd>) {
      if (!responseLogged) {
        responseLogged = true;
        const duration = Date.now() - startTime;

        const responseContext: ResponseLogContext = {
          ...requestContext,
          statusCode: res.statusCode,
          duration,
        };

        // Log based on status code
        if (res.statusCode >= 500) {
          logger.error('Request completed with server error', responseContext);
        } else if (res.statusCode >= 400) {
          logger.warn('Request completed with client error', responseContext);
        } else {
          logger.info('Request completed', responseContext);
        }
      }

      return originalEnd.apply(this, args);
    } as typeof originalEnd;

    try {
      // Execute the handler
      await handler(req, res);
    } catch (error) {
      const duration = Date.now() - startTime;

      // Log error
      logger.error(
        'Request failed with exception',
        error instanceof Error ? error : new Error(String(error)),
        {
          ...requestContext,
          duration,
        }
      );

      // Re-throw to let Next.js handle the error
      throw error;
    }
  };
}

/**
 * Create a logger with request context for use within API handlers
 */
export function getRequestLogger(req: NextApiRequest): Logger {
  const requestId = (req.headers['x-request-id'] as string) || generateRequestId();
  return createLogger({
    requestId,
    method: req.method,
    path: req.url,
  });
}

// Export types
export type { RequestLogContext, ResponseLogContext };
