import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flux_localization/flux_localization.dart';

import '../../common/config.dart';
import '../../common/tools.dart';
import '../entities/product.dart';
import '../entities/product_variation.dart';
import 'cart_base.dart';
import 'cart_item_meta_data.dart';
import 'mixin/index.dart';

class CartModelMagento
    with
        ChangeNotifier,
        CartMixin,
        CouponMixin,
        CurrencyMixin,
        AddressMixin,
        LocalMixin,
        VendorMixin,
        MagentoMixin,
        OrderDeliveryMixin
    implements CartModel {
  static final CartModelMagento _instance = CartModelMagento._internal();

  factory CartModelMagento() => _instance;

  CartModelMagento._internal();

  @override
  Future<void> initData() async {
    resetValues();
    await getAddress();
    getCurrency();
  }

  @override
  double getSubTotal() {
    return productsInCart.keys.fold(0.0, (sum, key) {
      var productVariation = cartItemMetaDataInCart[key]?.variation;
      if (productVariation?.price?.isNotEmpty ?? false) {
        return sum +
            double.parse(productVariation!.price!) * productsInCart[key]!;
      } else {
        var productId = Product.cleanProductID(key);

        var price = PriceTools.getPriceProductValue(
          item[productId],
          onSale: true,
        )!;
        if (price.isNotEmpty) {
          return sum + double.parse(price) * productsInCart[key]!;
        }
        return sum;
      }
    });
  }

  /// Magento: get item total
  @override
  double getItemTotal({
    ProductVariation? productVariation,
    Product? product,
    int quantity = 1,
  }) {
    var subtotal = double.parse(product!.price!) * quantity;
    if (discountAmount > 0) {
      return subtotal - discountAmount;
    } else {
      if (couponObj != null) {
        if (couponObj!.discountType == 'percent') {
          return subtotal - subtotal * couponObj!.amount! / 100;
        } else {
          return subtotal - (couponObj!.amount! * quantity);
        }
      } else {
        return subtotal;
      }
    }
  }

  /// Magento: get coupon
  @override
  String getCoupon() {
    if (discountAmount > 0) {
      return '-${PriceTools.getCurrencyFormatted(discountAmount, currencyRates, currency: currencyCode)!}';
    } else {
      if (couponObj != null) {
        if (couponObj!.discountType == 'percent') {
          return '-${couponObj!.amount}%';
        } else {
          return '-${PriceTools.getCurrencyFormatted(couponObj!.amount! * totalCartQuantity, currencyRates, currency: currencyCode)!}';
        }
      } else {
        return '';
      }
    }
  }

  /// Magento: get total
  @override
  double getTotal() {
    var subtotal = getSubTotal();

    if (discountAmount > 0) {
      subtotal -= discountAmount;
    } else {
      if (couponObj != null) {
        if (couponObj!.discountType == 'percent') {
          subtotal -= subtotal * couponObj!.amount! / 100;
        } else {
          subtotal -= (couponObj!.amount! * totalCartQuantity);
        }
      }
    }
    if (kPaymentConfig.enableShipping) {
      subtotal += getShippingCost()!;
    }
    if (taxes.isNotEmpty) {
      subtotal += taxesTotal;
    }
    subtotal += getCODExtraFee();
    return subtotal;
  }

  /// Magento: get coupon cost
  @override
  double getCouponCost() {
    if (discountAmount > 0) {
      return discountAmount;
    } else {
      var subtotal = getSubTotal();
      if (couponObj != null) {
        if (couponObj!.discountType == 'percent') {
          return subtotal * couponObj!.amount! / 100;
        } else {
          return couponObj!.amount! * totalCartQuantity;
        }
      } else {
        return 0.0;
      }
    }
  }

  @override
  String updateQuantity(Product product, String key, int quantity, {context}) {
    var message = '';
    var total = quantity;
    ProductVariation? variation;

    if (key.contains('-')) {
      variation = getProductVariationById(key);
    }
    var stockQuantity = variation == null
        ? product.stockQuantity
        : variation.stockQuantity;

    if (!product.manageStock) {
      productsInCart[key] = total;
    } else if (total <= stockQuantity!) {
      if (product.minQuantity == null && product.maxQuantity == null) {
        productsInCart[key] = total;
      } else if (product.minQuantity != null && product.maxQuantity == null) {
        total < product.minQuantity!
            ? message = 'Minimum quantity is ${product.minQuantity}'
            : productsInCart[key] = total;
      } else if (product.minQuantity == null && product.maxQuantity != null) {
        total > product.maxQuantity!
            ? message =
                  'You can only purchase ${product.maxQuantity} for this product'
            : productsInCart[key] = total;
      } else if (product.minQuantity != null && product.maxQuantity != null) {
        if (total >= product.minQuantity! && total <= product.maxQuantity!) {
          productsInCart[key] = total;
        } else {
          if (total < product.minQuantity!) {
            message = 'Minimum quantity is ${product.minQuantity}';
          }
          if (total > product.maxQuantity!) {
            message =
                'You can only purchase ${product.maxQuantity} for this product';
          }
        }
      }
    } else {
      message = 'Currently we only have $stockQuantity of this product';
    }
    if (message.isEmpty) {
      updateQuantityCartLocal(key: key, quantity: quantity);
      notifyListeners();
    }
    return message;
  }

  @override
  // Removes an item from the cart.
  void removeItemFromCart(String key) {
    if (productsInCart.containsKey(key)) {
      productsInCart.remove(key);
      cartItemMetaDataInCart.remove(key);
      productSkuInCart.remove(key);
      removeProductLocal(key);
    }
    notifyListeners();
  }

  // Removes everything from the cart.
  @override
  Future<void> clearCart({isSaveRemote = true, isSaveLocal = true}) async {
    if (isSaveLocal) {
      await clearCartLocal();
    }
    productsInCart.clear();
    item.clear();
    cartItemMetaDataInCart.clear();
    productSkuInCart.clear();
    shippingMethod = null;
    paymentMethod = null;
    resetCoupon();
    notes = null;
    discountAmount = 0.0;
    notifyListeners();
  }

  @override
  void setOrderNotes(String note) {
    notes = note;
    notifyListeners();
  }

  @override
  FutureOr<(bool, String)> addProductToCart({
    required BuildContext context,
    required Product product,
    int quantity = 1,
    Function? notify,
    isSaveLocal = true,
    isSaveRemote = true,
    CartItemMetaData? cartItemMetaData,
  }) async {
    if (product.type == 'configurable' && cartItemMetaData?.variation == null) {
      final message = S.of(context).pleaseSelectAllAttributes;
      return (false, message);
    }

    var (success, message) = await super.addProductToCart(
      context: context,
      product: product,
      quantity: quantity,
      cartItemMetaData: cartItemMetaData,
      isSaveLocal: isSaveLocal,
      notify: notifyListeners,
    );

    var key = product.id.toString();
    if (cartItemMetaData?.variation != null) {
      if (cartItemMetaData?.variation?.id != null) {
        key += '-${cartItemMetaData?.variation?.id}';
      }
      if (cartItemMetaData?.options != null) {
        for (var option in (cartItemMetaData?.options?.keys ?? [])) {
          key += '-$option${cartItemMetaData?.options?[option]}';
        }
      }
    }
    productSkuInCart[key] = cartItemMetaData?.variation != null
        ? cartItemMetaData?.variation?.sku
        : product.sku;
    return (success, message);
  }

  @override
  void setRewardTotal(double total) {
    rewardTotal = total;
    notifyListeners();
  }

  @override
  void updateProduct(String productId, Product? product) {
    super.updateProduct(productId, product);
    notifyListeners();
  }

  @override
  void updateProductVariant(
    String productId,
    ProductVariation? productVariant,
  ) {
    super.updateProductVariant(productId, productVariant);
    notifyListeners();
  }

  @override
  void updateStateCheckoutButton() {
    super.updateStateCheckoutButton();
    notifyListeners();
  }
}
