import 'package:equatable/equatable.dart';

/// Generic API response wrapper that handles the standard API response format
///
/// All API responses follow this structure:
/// ```json
/// {
///   "Value": T,
///   "IsSuccess": bool,
///   "IsFailure": bool,
///   "Errors": List<String>,
///   "ErrorCodes": List<String>
/// }
/// ```
///
/// Usage:
/// ```dart
/// final response = ApiResponse.fromJson(
///   jsonData,
///   (json) => User.fromJson(json),
/// );
///
/// if (response.isSuccess && response.value != null) {
///   // Handle success
///   final user = response.value!;
/// } else {
///   // Handle error
///   final errorMessage = response.errors.first;
/// }
/// ```
class ApiResponse<T> extends Equatable {
  /// The actual data/payload of the response
  /// Can be null if the API call failed or returned no data
  final T? value;

  /// Indicates if the API call was successful
  final bool isSuccess;

  /// Indicates if the API call failed
  final bool isFailure;

  /// List of error messages if the call failed
  final List<String> errors;

  /// List of error codes for programmatic error handling
  final List<String> errorCodes;

  const ApiResponse({
    this.value,
    required this.isSuccess,
    required this.isFailure,
    this.errors = const [],
    this.errorCodes = const [],
  });

  /// Create an ApiResponse from JSON
  ///
  /// The [fromJsonT] function is used to deserialize the "Value" field.
  /// If null, the value is used as-is.
  ///
  /// Example:
  /// ```dart
  /// // For single object
  /// ApiResponse.fromJson(json, (j) => User.fromJson(j))
  ///
  /// // For list of objects
  /// ApiResponse.fromJson(
  ///   json,
  ///   (j) => (j as List).map((e) => User.fromJson(e)).toList()
  /// )
  ///
  /// // For primitive types or no conversion needed
  /// ApiResponse.fromJson(json, null)
  /// ```
  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(dynamic)? fromJsonT,
  ) {
    return ApiResponse(
      value: json['Value'] != null && fromJsonT != null
          ? fromJsonT(json['Value'])
          : json['Value'] as T?,
      isSuccess: json['IsSuccess'] ?? false,
      isFailure: json['IsFailure'] ?? true,
      errors: json['Errors'] != null
          ? List<String>.from(json['Errors'])
          : const [],
      errorCodes: json['ErrorCodes'] != null
          ? List<String>.from(json['ErrorCodes'])
          : const [],
    );
  }

  /// Create a successful response (useful for testing or manual creation)
  factory ApiResponse.success(T value) {
    return ApiResponse(
      value: value,
      isSuccess: true,
      isFailure: false,
    );
  }

  /// Create a failed response (useful for testing or manual creation)
  factory ApiResponse.failure({
    required List<String> errors,
    List<String>? errorCodes,
  }) {
    return ApiResponse(
      isSuccess: false,
      isFailure: true,
      errors: errors,
      errorCodes: errorCodes ?? const [],
    );
  }

  /// Check if response has data
  bool get hasValue => value != null;

  /// Get the first error message if available
  String? get firstError => errors.isNotEmpty ? errors.first : null;

  /// Get the first error code if available
  String? get firstErrorCode => errorCodes.isNotEmpty ? errorCodes.first : null;

  /// Get a combined error message from all errors
  String get combinedErrorMessage {
    if (errors.isEmpty) return 'An unknown error occurred';
    return errors.join(', ');
  }

  /// Convert to a map (useful for serialization or debugging)
  Map<String, dynamic> toJson(Object? Function(T)? toJsonT) {
    return {
      'Value': value != null && toJsonT != null ? toJsonT(value as T) : value,
      'IsSuccess': isSuccess,
      'IsFailure': isFailure,
      'Errors': errors,
      'ErrorCodes': errorCodes,
    };
  }

  /// Create a copy with modified fields
  ApiResponse<T> copyWith({
    T? value,
    bool? isSuccess,
    bool? isFailure,
    List<String>? errors,
    List<String>? errorCodes,
  }) {
    return ApiResponse(
      value: value ?? this.value,
      isSuccess: isSuccess ?? this.isSuccess,
      isFailure: isFailure ?? this.isFailure,
      errors: errors ?? this.errors,
      errorCodes: errorCodes ?? this.errorCodes,
    );
  }

  @override
  List<Object?> get props => [value, isSuccess, isFailure, errors, errorCodes];

  @override
  String toString() {
    if (isSuccess) {
      return 'ApiResponse.success(value: $value)';
    } else {
      return 'ApiResponse.failure(errors: $errors, errorCodes: $errorCodes)';
    }
  }
}

/// Extension to convert ApiResponse to nullable value easily
extension ApiResponseExtension<T> on ApiResponse<T> {
  /// Get value if success, otherwise return null
  T? get valueOrNull => isSuccess ? value : null;

  /// Get value if success, otherwise throw exception with error message
  T get valueOrThrow {
    if (isSuccess && value != null) {
      return value!;
    }
    throw Exception(combinedErrorMessage);
  }
}

/// Specialized API response for list data with pagination
class PaginatedApiResponse<T> extends ApiResponse<List<T>> {
  /// Current page number
  final int currentPage;

  /// Total number of pages
  final int totalPages;

  /// Total number of items
  final int totalItems;

  /// Number of items per page
  final int pageSize;

  /// Whether there is a next page
  bool get hasNextPage => currentPage < totalPages;

  /// Whether there is a previous page
  bool get hasPreviousPage => currentPage > 1;

  const PaginatedApiResponse({
    super.value,
    required super.isSuccess,
    required super.isFailure,
    super.errors,
    super.errorCodes,
    required this.currentPage,
    required this.totalPages,
    required this.totalItems,
    required this.pageSize,
  });

  /// Create a PaginatedApiResponse from JSON
  factory PaginatedApiResponse.fromJson(
    Map<String, dynamic> json,
    List<T> Function(dynamic) fromJsonList,
  ) {
    final apiResponse = ApiResponse<List<T>>.fromJson(json, fromJsonList);

    return PaginatedApiResponse(
      value: apiResponse.value,
      isSuccess: apiResponse.isSuccess,
      isFailure: apiResponse.isFailure,
      errors: apiResponse.errors,
      errorCodes: apiResponse.errorCodes,
      currentPage: json['CurrentPage'] ?? 1,
      totalPages: json['TotalPages'] ?? 1,
      totalItems: json['TotalItems'] ?? 0,
      pageSize: json['PageSize'] ?? 20,
    );
  }

  @override
  List<Object?> get props => [
    ...super.props,
    currentPage,
    totalPages,
    totalItems,
    pageSize,
  ];
}
