/// Remote Data Source: Cart API
///
/// Handles all cart-related API requests to the backend.
/// Uses Frappe/ERPNext API endpoints for cart operations.
library;

import 'package:dio/dio.dart';

import 'package:worker/core/constants/api_constants.dart';
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/features/cart/data/models/cart_item_model.dart';

/// Cart Remote Data Source Interface
abstract class CartRemoteDataSource {
  /// Add items to cart
  ///
  /// [items] - List of items with item_id, quantity, and amount
  /// Returns true if successful
  Future<bool> addToCart({
    required List<Map<String, dynamic>> items,
  });

  /// Remove items from cart
  ///
  /// [itemIds] - List of product ERPNext item codes to remove
  /// Returns true if successful
  Future<bool> removeFromCart({
    required List<String> itemIds,
  });

  /// Get user's cart items
  ///
  /// [limitStart] - Pagination offset (default: 0)
  /// [limitPageLength] - Page size (default: 0 for all)
  /// Returns list of cart items
  Future<List<CartItemModel>> getUserCart({
    int limitStart = 0,
    int limitPageLength = 0,
  });
}

/// Cart Remote Data Source Implementation
class CartRemoteDataSourceImpl implements CartRemoteDataSource {
  CartRemoteDataSourceImpl(this._dioClient);

  final DioClient _dioClient;

  @override
  Future<bool> addToCart({
    required List<Map<String, dynamic>> items,
  }) async {
    try {
      // Build request body
      final requestBody = {
        'items': items,
      };

      // Make API request
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.addToCart}',
        data: requestBody,
      );

      // Check response status
      if (response.statusCode != 200 && response.statusCode != 201) {
        throw ServerException(
          'Failed to add items to cart',
          response.statusCode,
        );
      }

      // Parse response
      // Expected format: { "message": [{ "item_id": "...", "success": true, "message": "..." }] }
      final responseData = response.data;

      if (responseData == null) {
        throw const ParseException('Invalid response format from add to cart API');
      }

      return true;
    } on DioException catch (e) {
      throw _handleDioException(e);
    } catch (e) {
      if (e is NetworkException ||
          e is ServerException ||
          e is ParseException) {
        rethrow;
      }
      throw UnknownException('Failed to add items to cart', e);
    }
  }

  @override
  Future<bool> removeFromCart({
    required List<String> itemIds,
  }) async {
    try {
      // Build request body
      final requestBody = {
        'item_ids': itemIds,
      };

      // Make API request
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.removeFromCart}',
        data: requestBody,
      );

      // Check response status
      if (response.statusCode != 200) {
        throw ServerException(
          'Failed to remove items from cart',
          response.statusCode,
        );
      }

      // Parse response
      // Expected format: { "message": [{ "item_id": "...", "success": true, "message": "..." }] }
      final responseData = response.data;

      if (responseData == null) {
        throw const ParseException('Invalid response format from remove from cart API');
      }

      final message = responseData['message'];
      if (message is List && message.isNotEmpty) {
        // Check if all items were removed successfully
        final allSuccess = message.every((item) => item['success'] == true);
        return allSuccess;
      }

      return true;
    } on DioException catch (e) {
      throw _handleDioException(e);
    } catch (e) {
      if (e is NetworkException ||
          e is ServerException ||
          e is ParseException) {
        rethrow;
      }
      throw UnknownException('Failed to remove items from cart', e);
    }
  }

  @override
  Future<List<CartItemModel>> getUserCart({
    int limitStart = 0,
    int limitPageLength = 0,
  }) async {
    try {
      // Build request body
      final requestBody = {
        'limit_start': limitStart,
        'limit_page_length': limitPageLength,
      };

      // Make API request
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getUserCart}',
        data: requestBody,
      );

      // Check response status
      if (response.statusCode != 200) {
        throw ServerException(
          'Failed to get cart items',
          response.statusCode,
        );
      }

      // Parse response
      // Expected format: { "message": [{ "name": "...", "item": "...", "quantity": 0, "amount": 0, ... }] }
      final responseData = response.data;

      if (responseData == null) {
        throw const ParseException('Invalid response format from get user cart API');
      }

      final message = responseData['message'];
      if (message == null || message is! List) {
        throw const ParseException('Invalid message format in get user cart response');
      }

      // Convert to CartItemModel list
      final cartItems = <CartItemModel>[];
      for (final item in message) {
        if (item is! Map<String, dynamic>) continue;

        try {
          // Map API response to CartItemModel
          // API fields: name, item, quantity, amount, item_code, item_name, image, conversion_of_sm
          final quantity = (item['quantity'] as num?)?.toDouble() ?? 0.0;
          final unitPrice = (item['amount'] as num?)?.toDouble() ?? 0.0;

          final cartItem = CartItemModel(
            cartItemId: item['name'] as String? ?? '',
            cartId: 'user_cart', // Fixed cart ID for user's cart
            productId: item['item_code'] as String? ?? item['item'] as String? ?? '',
            quantity: quantity,
            unitPrice: unitPrice,
            subtotal: quantity * unitPrice,
            addedAt: DateTime.now(), // API doesn't provide timestamp
            // Product details from cart API - no need to fetch separately
            itemName: item['item_name'] as String?,
            image: item['image'] as String?,
            conversionOfSm: (item['conversion_of_sm'] as num?)?.toDouble(),
          );

          cartItems.add(cartItem);
        } catch (e) {
          // Skip invalid items but don't fail the whole request
          continue;
        }
      }

      return cartItems;
    } on DioException catch (e) {
      throw _handleDioException(e);
    } catch (e) {
      if (e is NetworkException ||
          e is ServerException ||
          e is ParseException) {
        rethrow;
      }
      throw UnknownException('Failed to get cart items', e);
    }
  }

  /// Handle Dio exceptions and convert to custom exceptions
  Exception _handleDioException(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        return const TimeoutException();

      case DioExceptionType.connectionError:
        return const NoInternetException();

      case DioExceptionType.badResponse:
        final statusCode = e.response?.statusCode;
        if (statusCode != null) {
          if (statusCode == 401) {
            return const UnauthorizedException();
          } else if (statusCode == 403) {
            return const ForbiddenException();
          } else if (statusCode == 404) {
            return const NotFoundException('Cart not found');
          } else if (statusCode == 429) {
            return const RateLimitException();
          } else if (statusCode >= 500) {
            return ServerException(
              'Server error: ${e.response?.statusMessage ?? "Unknown error"}',
              statusCode,
            );
          }
        }
        return NetworkException(
          e.response?.statusMessage ?? 'Network error',
          statusCode: statusCode,
        );

      case DioExceptionType.cancel:
        return const NetworkException('Request cancelled');

      case DioExceptionType.badCertificate:
        return const NetworkException('Invalid SSL certificate');

      case DioExceptionType.unknown:
        return const NoInternetException();
    }
  }
}
