import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

/**
 * Standardized API Response Interface
 *
 * This interface defines a consistent response structure for all API endpoints.
 * Using a standardized format improves API consumer experience and simplifies
 * client-side error handling.
 *
 * @template T - The type of data being returned
 */
export interface Response<T> {
  /** Indicates whether the request was successful */
  success: boolean;
  /** Error details if success is false, null otherwise */
  errors: any;
  /** The actual response data on success, or error details on failure */
  data: T;
}

/**
 * TransformInterceptor - Standardizes API response format
 *
 * This interceptor wraps all API responses in a consistent structure and
 * normalizes error handling. It ensures that all endpoints return data
 * in the same format, making the API more predictable and easier to consume.
 *
 * Features:
 * - Wraps successful responses in { success: true, errors: null, data: ... }
 * - Normalizes error responses to consistent format
 * - Handles both explicit HttpExceptions and unhandled errors
 * - Extracts data from nested structures when needed
 *
 * @template T - The type of data being returned
 *
 * @example
 * // Apply globally for consistent API responses
 * app.useGlobalInterceptors(new TransformInterceptor());
 *
 * // Successful response example:
 * // Original: { id: 1, name: 'John' }
 * // Transformed: { success: true, errors: null, data: { id: 1, name: 'John' } }
 *
 * // Error response example:
 * // Original: HttpException with message 'User not found'
 * // Transformed: { success: false, errors: 'User not found', data: null }
 */
@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, Response<T>>
{
  /**
   * Intercepts HTTP responses and transforms them to standardized format
   *
   * @param context - ExecutionContext with request/response details
   * @param next - CallHandler to continue processing
   * @returns Observable with transformed response
   */
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      // Transform successful responses
      map((...args) => {
        // Extract the response data from various possible formats
        // args[0] could be the raw response or an object containing data property
        const responseData = args[0]?.data ?? args[0];

        return {
          success: true,
          errors: null,
          data: responseData,
        };
      }),

      // Handle and normalize errors
      catchError((...error) => {
        const errorObj = error[0];

        // Extract status code with fallback to BAD_REQUEST (400)
        const statusCode = errorObj.status ?? HttpStatus.BAD_REQUEST;

        // Extract error message/response from various error formats
        // Priority: 1. error.response, 2. error.message, 3. error object itself
        const errorResponse = errorObj.response ?? errorObj.message ?? errorObj;

        // Throw a standardized HttpException
        throw new HttpException(
          {
            success: false,
            errors: errorResponse,
            data: null,
          },
          statusCode,
          {},
        );
      }),
    );
  }

  /**
   * Helper method to create success response format
   * (Note: This method is currently unused in the intercept method)
   *
   * @param data - The data to wrap in success response
   * @returns Standardized success response object
   */
  private success(data: any) {
    return { success: true, errors: null, data };
  }
}

/**
 * Implementation Notes and Best Practices:
 *
 * 1. **Response Format Consistency**:
 *    - All endpoints return same structure: { success, errors, data }
 *    - Makes client-side handling predictable
 *    - Simplifies error handling logic on frontend
 *
 * 2. **Error Handling Improvements**:
 *    Consider enhancing error normalization for different error types:
 *
 *    ```typescript
 *    catchError((error) => {
 *      if (error instanceof HttpException) {
 *        // Handle NestJS HTTP exceptions
 *        const response = error.getResponse();
 *        const status = error.getStatus();
 *        // ... transform
 *      } else if (error instanceof Error) {
 *        // Handle generic JavaScript errors
 *        // ... transform
 *      } else {
 *        // Handle unknown error types
 *        // ... transform
 *      }
 *    })
 *    ```
 *
 * 3. **Performance Considerations**:
 *    - Minimal overhead added per request
 *    - Consider excluding certain endpoints (health checks, static files)
 *    - Use conditional logic based on request context if needed
 *
 * 4. **Testing Strategy**:
 *    - Test with various data structures (objects, arrays, null, primitive values)
 *    - Test error responses with different exception types
 *    - Verify status codes are preserved correctly
 *    - Test nested data extraction (data property vs direct value)
 *
 * 5. **Potential Enhancements**:
 *    - Add request ID correlation
 *    - Include metadata (timestamp, API version, pagination info)
 *    - Support for different response formats based on Accept header
 *    - Exclude certain routes from transformation
 *    - Add logging for monitoring purposes
 *
 * 6. **Common Use Cases**:
 *    - Mobile/Web API clients that expect consistent responses
 *    - Microservices communication standardization
 *    - Third-party API integrations
 *    - Automated API documentation generation
 */
