/// Repository Implementation: Cart Repository
///
/// Implements the cart repository interface with:
/// - API-first strategy with local fallback
/// - Automatic sync between API and local storage
/// - Offline queue support
/// - Error handling and recovery
library;

import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/features/cart/data/datasources/cart_local_datasource.dart';
import 'package:worker/features/cart/data/datasources/cart_remote_datasource.dart';
import 'package:worker/features/cart/data/models/cart_item_model.dart';
import 'package:worker/features/cart/domain/entities/cart_item.dart';
import 'package:worker/features/cart/domain/repositories/cart_repository.dart';

/// Cart Repository Implementation
///
/// Strategy: API-first with local fallback
/// 1. Try API request first
/// 2. On success, sync to local storage
/// 3. On failure, fallback to local data (for reads)
/// 4. Queue failed writes for later sync
class CartRepositoryImpl implements CartRepository {
  CartRepositoryImpl({
    required CartRemoteDataSource remoteDataSource,
    required CartLocalDataSource localDataSource,
  })  : _remoteDataSource = remoteDataSource,
        _localDataSource = localDataSource;

  final CartRemoteDataSource _remoteDataSource;
  final CartLocalDataSource _localDataSource;

  @override
  Future<bool> addToCart({
    required List<String> itemIds,
    required List<double> quantities,
    required List<double> prices,
    List<double?>? conversionFactors,
  }) async {
    try {
      // Validate input
      if (itemIds.length != quantities.length || itemIds.length != prices.length) {
        throw const ValidationException(
          'Item IDs, quantities, and prices must have the same length',
        );
      }

      // Build API request items
      final items = <Map<String, dynamic>>[];
      for (int i = 0; i < itemIds.length; i++) {
        final item = <String, dynamic>{
          'item_id': itemIds[i],
          'quantity': quantities[i],
          'amount': prices[i],
        };
        // Add conversion_of_sm if provided
        if (conversionFactors != null && i < conversionFactors.length) {
          item['conversion_of_sm'] = conversionFactors[i] ?? 0.0;
        }
        items.add(item);
      }

      // Try API first
      try {
        final success = await _remoteDataSource.addToCart(items: items);

        // Also save to local storage for offline access
        if (success) {
          for (int i = 0; i < itemIds.length; i++) {
            final cartItemModel = _createCartItemModel(
              productId: itemIds[i],
              quantity: quantities[i],
              unitPrice: prices[i],
              conversionOfSm: conversionFactors?[i],
            );
            await _localDataSource.addCartItem(cartItemModel);
          }
        }

        return success;
      } on NetworkException catch (e) {
        // If no internet, add to local cart only
        if (e is NoInternetException || e is TimeoutException) {
          for (int i = 0; i < itemIds.length; i++) {
            final cartItemModel = _createCartItemModel(
              productId: itemIds[i],
              quantity: quantities[i],
              unitPrice: prices[i],
              conversionOfSm: conversionFactors?[i],
            );
            await _localDataSource.addCartItem(cartItemModel);
          }

          // TODO: Queue for sync when online

          return true;
        }
        rethrow;
      }
    } on StorageException {
      rethrow;
    } on ValidationException {
      rethrow;
    } on ServerException {
      rethrow;
    } on NetworkException {
      rethrow;
    } catch (e) {
      throw UnknownException('Failed to add items to cart', e);
    }
  }

  @override
  Future<bool> removeFromCart({
    required List<String> itemIds,
  }) async {
    try {
      // Try API first
      try {
        final success = await _remoteDataSource.removeFromCart(itemIds: itemIds);

        if (success) {
          // Sync to local storage
          await _localDataSource.removeCartItems(itemIds);
        }

        return success;
      } on NetworkException catch (e) {
        // If no internet, remove from local cart only
        if (e is NoInternetException || e is TimeoutException) {
          await _localDataSource.removeCartItems(itemIds);

          // TODO: Queue for sync when online

          return true;
        }
        rethrow;
      }
    } on StorageException {
      rethrow;
    } on ServerException {
      rethrow;
    } on NetworkException {
      rethrow;
    } catch (e) {
      throw UnknownException('Failed to remove items from cart', e);
    }
  }

  @override
  Future<List<CartItem>> getCartItems() async {
    try {
      // Try API first
      try {
        final cartItemModels = await _remoteDataSource.getUserCart();

        // Sync to local storage
        await _localDataSource.saveCartItems(cartItemModels);

        // Convert to domain entities
        return cartItemModels.map(_modelToEntity).toList();
      } on NetworkException catch (e) {
        // If no internet, fallback to local storage
        if (e is NoInternetException || e is TimeoutException) {
          final localItems = await _localDataSource.getCartItems();
          return localItems.map(_modelToEntity).toList();
        }
        rethrow;
      }
    } on StorageException {
      rethrow;
    } on ServerException {
      rethrow;
    } on NetworkException {
      rethrow;
    } catch (e) {
      throw UnknownException('Failed to get cart items', e);
    }
  }

  @override
  Future<bool> updateQuantity({
    required String itemId,
    required double quantity,
    required double price,
    double? conversionFactor,
  }) async {
    try {
      // API doesn't have update endpoint, use add with new quantity
      // This will replace the existing quantity
      return await addToCart(
        itemIds: [itemId],
        quantities: [quantity],
        prices: [price],
        conversionFactors: conversionFactor != null ? [conversionFactor] : null,
      );
    } catch (e) {
      throw UnknownException('Failed to update cart item quantity', e);
    }
  }

  @override
  Future<bool> clearCart() async {
    try {
      // Get all cart items
      final items = await getCartItems();
      if (items.isEmpty) {
        return true;
      }

      // Extract item IDs
      final itemIds = items.map((item) => item.productId).toList();

      // Remove all items
      return await removeFromCart(itemIds: itemIds);
    } catch (e) {
      throw UnknownException('Failed to clear cart', e);
    }
  }

  @override
  Future<double> getCartTotal() async {
    try {
      // Try to calculate from API data first
      try {
        final items = await getCartItems();
        return items.fold<double>(
          0.0,
          (sum, item) => sum + item.subtotal,
        );
      } on NetworkException catch (e) {
        // If no internet, use local calculation
        if (e is NoInternetException || e is TimeoutException) {
          return await _localDataSource.getCartTotal();
        }
        rethrow;
      }
    } catch (e) {
      throw UnknownException('Failed to get cart total', e);
    }
  }

  @override
  Future<int> getCartItemCount() async {
    try {
      // Try to calculate from API data first
      try {
        final items = await getCartItems();
        final totalQuantity = items.fold<double>(
          0.0,
          (sum, item) => sum + item.quantity,
        );
        return totalQuantity.toInt();
      } on NetworkException catch (e) {
        // If no internet, use local calculation
        if (e is NoInternetException || e is TimeoutException) {
          return await _localDataSource.getCartItemCount();
        }
        rethrow;
      }
    } catch (e) {
      throw UnknownException('Failed to get cart item count', e);
    }
  }

  // ============================================================================
  // Helper Methods
  // ============================================================================

  /// Convert CartItemModel to CartItem entity
  CartItem _modelToEntity(CartItemModel model) {
    return CartItem(
      cartItemId: model.cartItemId,
      cartId: model.cartId,
      productId: model.productId,
      quantity: model.quantity,
      unitPrice: model.unitPrice,
      subtotal: model.subtotal,
      addedAt: model.addedAt,
      itemName: model.itemName,
      image: model.image,
      conversionOfSm: model.conversionOfSm,
    );
  }

  /// Create CartItemModel from parameters
  CartItemModel _createCartItemModel({
    required String productId,
    required double quantity,
    required double unitPrice,
    double? conversionOfSm,
  }) {
    return CartItemModel(
      cartItemId: DateTime.now().millisecondsSinceEpoch.toString(),
      cartId: 'user_cart',
      productId: productId,
      quantity: quantity,
      unitPrice: unitPrice,
      subtotal: quantity * unitPrice,
      addedAt: DateTime.now(),
      conversionOfSm: conversionOfSm,
    );
  }
}
