import 'dart:async';
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_wisetronic/models/product.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hive/hive.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart' as path;
import 'package:universal_io/io.dart';
import 'package:url_launcher/url_launcher.dart';

import '../constants.dart';
import '../events/eventbus.dart';
import '../events/events.dart';
import '../generated/l10n.dart';
import '../models/business.dart';
import '../models/cart_info.dart';
import '../models/cart_line_item.dart';
import '../models/order.dart';
import '../models/user.dart';
import '../routes.dart';
import '../store/actions.dart';
import '../store/store.dart';
import 'http_util.dart';
import 'util_web.dart' if (dart.library.io) 'util_io.dart';

typedef void OnSuccess(Response response);
typedef void OnError(dynamic error);
typedef void OnComplete(dynamic data);

typedef void OnOk();
typedef void OnLoadImageError();

class Utils {
  static StreamSubscription? onProductWillAddToCartSubscription;
  static StreamSubscription? onProductWillRemoveFromCartSubscription;

  init() async {
    onProductWillAddToCartSubscription =
        eventBus.on<OnProductWillAddToCart>().listen((event) {
          CartInfo? found = getCartInfoByBusiness(store.state.cartInfos, event.business);
          final List<CartLineItem> existing =
              (found != null && found.productList != null && found.productList!.isNotEmpty)
                  ? found.productList! : <CartLineItem>[];
          CartInfo cartInfo = found ?? CartInfo();
          if (existing.isEmpty) {
            cartInfo.id = 0;
            cartInfo.amountPaid = 0.0;
            cartInfo.businessInfo = event.business;
            cartInfo.extraFeeList = [];
            cartInfo.discountList = [];
            CartLineItem lineItem = newCartLineItem(
                id: 0, price: event.price, product: event.product,
                name: event.product.name, description: event.description, quantity: 1.0);
            cartInfo.productList = [lineItem];
            addSubproductToCard(cartInfo, lineItem);
            store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
            eventBus.fire(OnCartInfoUpdated());
          } else {
            if ((event.product.productAttributes?.length ?? 0) > 0) {
              CartLineItem lineItem = newCartLineItem(
                  id: 0, price: event.price, product: event.product,
                  name: event.product.name, description: event.description, quantity: 1.0);
              existing.add(lineItem);
              addSubproductToCard(cartInfo, lineItem);
              store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
              eventBus.fire(OnCartInfoUpdated());
            } else {
              int foundIndex = -1;
              for (var i = 0; i < existing.length; i++) {
                if (existing[i].product?.id == event.product.id) { foundIndex = i; break; }
              }
              if (foundIndex == -1) {
                CartLineItem lineItem = newCartLineItem(
                    id: 0, price: event.price, product: event.product,
                    name: event.product.name, description: event.description, quantity: 1.0);
                existing.add(lineItem);
                addSubproductToCard(cartInfo, lineItem);
                store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
                eventBus.fire(OnCartInfoUpdated());
              } else {
                if ((existing[foundIndex].quantity ?? 0) + 1.0 > (event.product.leftNum ?? double.infinity)) {
                  // insufficient stock
                } else {
                  existing[foundIndex].quantity = (existing[foundIndex].quantity ?? 0) + 1;
                  addSubproductQty(cartInfo, existing[foundIndex]);
                  store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
                  eventBus.fire(OnCartInfoUpdated());
                }
              }
            }
          }
        });
    onProductWillRemoveFromCartSubscription =
        eventBus.on<OnProductWillRemoveFromCart>().listen((event) {
          CartInfo? found = getCartInfoByBusiness(store.state.cartInfos, event.business);
          if (found != null && found.productList != null) {
            final existing = found.productList!;
            if (existing.isNotEmpty) {
              if (event.productListIndex != -1) {
                if ((existing[event.productListIndex].quantity ?? 0) <= 1) {
                  String uuid = existing[event.productListIndex].uuid ?? '';
                  existing.removeAt(event.productListIndex);
                  removeSubproduct(found, uuid);
                } else {
                  existing[event.productListIndex].quantity = (existing[event.productListIndex].quantity ?? 0) - 1;
                  addSubproductQty(found, existing[event.productListIndex], remove: true);
                }
              } else {
                int idx = -1;
                for (var i = 0; i < existing.length; i++) {
                  if (existing[i].product?.id == event.product.id) { idx = i; break; }
                }
                if (idx != -1) {
                  if ((existing[idx].quantity ?? 0) <= 1) {
                    String uuid = existing[idx].uuid ?? '';
                    existing.removeAt(idx);
                    removeSubproduct(found, uuid);
                  } else {
                    existing[idx].quantity = (existing[idx].quantity ?? 0) - 1;
                    addSubproductQty(found, existing[idx], remove: true);
                  }
                }
              }
            }
            if (existing.isEmpty) {
              store.dispatch(UpdateCartInfo(removeCartInfoFromCartInfoList(store.state.cartInfos, found)));
            } else {
              store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, found)));
            }
            eventBus.fire(OnCartInfoUpdated());
          }
        });
  }
  static bool equalsIgnoreCase(String a, String b) =>
      (a == null && b == null) ||
      (a != null && b != null && a.toLowerCase() == b.toLowerCase());

  static int selectionsContains(
      Map<String, dynamic> selections, String key, String name) {
    if (selections.containsKey(key.toUpperCase())) {
      for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) {
        Map<String, dynamic> item = (selections[key.toUpperCase()] as List)[i];
        if (Utils.equalsIgnoreCase(item['name'], name)) {
          return i;
        }
      }
    }
    return -1;
  }

  static List<String> getSelectedAttributeValue(
      Map<String, dynamic> selections, String key) {
    List<String> valueArr = [];
    if (selections.containsKey(key.toUpperCase())) {
      for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) {
        valueArr.add(
            (selections[key.toUpperCase()][i]['name'] as String).toLowerCase());
      }
    }
    return valueArr;
  }

  static bool selectionsNotEmptyAt(
      Map<String, dynamic> selections, String key) {
    if (selections.containsKey(key.toUpperCase()) &&
        (selections[key.toUpperCase()] as List).length > 0) {
      return true;
    }
    return false;
  }

  static Map<String, dynamic>? stringToJson(String string) {
    if (string == null || string.isEmpty) {
      return null;
    }
    return json.decode(string);
  }

  static void jsonPrettyPrint(Map<String, dynamic> map) {
    JsonEncoder encoder = new JsonEncoder.withIndent('    ');
    String prettyPrint = encoder.convert(map);
    debugPrint(prettyPrint);
  }

  static launchURL(String url) async {
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }

  static String getPlatformName() {
    String platformName = '';
    if (kIsWeb) {
      platformName = 'web';
    } else if (Platform.isAndroid) {
      platformName = 'android';
    } else if (Platform.isIOS) {
      platformName = 'ios';
    }
    return platformName;
  }

  static Future<Box> getBox() async {
    return Util.getBox();
  }

  static Widget imageLoadingIndicator(
      {double width = 30.0, double height = 30.0}) {
    return SizedBox(
      width: width,
      height: height,
      child: Center(
        child: Container(
          width: 30.0,
          height: 30.0,
          child: CupertinoActivityIndicator(),
          color: Colors.transparent,
        ),
      ),
    );
  }

  static String safePhoneNumber(String phone) {
    if (phone.length < 8) {
      return phone;
    }
    String start = phone.substring(0, 3);
    String end = phone.substring(phone.length - 3);
    return '${start}****${end}';
  }

  static String safeString(String string) {
    if (string == null || string.length == 0) {
      return '';
    }
    String start = string.substring(0, 1);
    String end = string.substring(string.length - 1);
    return '${start}****${end}';
  }

  static String smartRound(double amount, int decimalPlace) {
    double a = double.parse(amount.toStringAsFixed(decimalPlace));
    double b = double.parse(amount.toStringAsFixed(0));
    if (a - b == 0) {
      return amount.toStringAsFixed(0);
    }
    return amount.toStringAsFixed(decimalPlace);
  }


  static showSubmitDialog(BuildContext context) {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          content: WillPopScope(
              child: Container(
                width: 300.0,
                height: 150.0,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      SpinKitThreeBounce(
                        color: Colors.lightBlueAccent,
                        size: 30.0,
                      ),
                      Text(
                        S.of(context).submitting,
                      ),
                    ],
                  ),
                ),
              ),
              onWillPop: () async {
                Fluttertoast.showToast(
                    msg: S.of(context).submitting_please_wait,
                    toastLength: Toast.LENGTH_SHORT,
                    gravity: ToastGravity.CENTER,
                    backgroundColor: Colors.red,
                    textColor: Colors.white);
                return false;
              }),
        );
      },
    );
  }

  static showLoadingDialog(BuildContext context, {String? message}) {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          content: WillPopScope(
              child: Container(
                width: 80.0,
                height: 80.0,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      SpinKitThreeBounce(
                        color: Colors.lightBlueAccent,
                        size: 30.0,
                      ),
                      Text(
                        message != null ? message : S.of(context).recalculating,
                      ),
                    ],
                  ),
                ),
              ),
              onWillPop: () async {
                Fluttertoast.showToast(
                    msg: S.of(context).loading_please_wait,
                    toastLength: Toast.LENGTH_SHORT,
                    gravity: ToastGravity.CENTER,
                    backgroundColor: Colors.red,
                    textColor: Colors.white);
                return false;
              }),
        );
      },
    );
  }

  static getTitleFromBody(String body) {
    if (body.length <= 30) {
      return body;
    }
    return body.substring(0, 29);
  }

  static void getMiniLink(
      String realLink, OnSuccess onSuccess, OnError onError) {
    HttpUtil.httpPost(
      'get-minilink/',
      (response) {
        onSuccess(response);
      },
      body: {'link': realLink},
      isFormData: true,
    ).catchError((error) {
      onError(error);
    });
  }

  static String getOs({bool checkWeb = false}) {
    if (checkWeb && kIsWeb) {
      return 'web';
    }
    if (Platform.isAndroid) {
      return 'android';
    }
    if (Platform.isIOS) {
      return 'ios';
    }
    if (Platform.isLinux) {
      return 'ubuntu';
    }
    if (Platform.isMacOS) {
      return 'mac';
    }
    if (Platform.isWindows) {
      return 'windows';
    }
    return 'web';
  }

  static int getOsFontHex(String os) {
    if (os == 'mac') {
      return Constants.FONT_APPLE;
    }
    if (os == 'windows') {
      return Constants.FONT_WINDOWS;
    }
    if (os == 'ubuntu') {
      return Constants.FONT_UBUNTU;
    }
    if (os == 'android') {
      return Constants.FONT_ANDROID;
    }
    if (os == 'ios') {
      return Constants.FONT_IOS;
    }
    return Constants.FONT_TOPTONS;
  }

  static void getCurrentUser() {
    getBox().then((box) {
      int userId = box.get(Constants.KEY_USER_ID, defaultValue: 0);
      String accessToken =
          box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
      if (userId == 0 || accessToken.isEmpty) {
        eventBus.fire(new OnGetCurrentUserFailed(Error()));
      } else {
        HttpUtil.httpGet(
          'v1/users/$userId',
          queryParameters: {},
          businessId: Constants.BUSINESS_ID,
        ).then((value) {
          User user = User.fromJson(value);
          store.dispatch(UpdateCurrentUser(user));
          eventBus.fire(OnCurrentUserUpdated());
        }).catchError((err) {
          eventBus.fire(new OnGetCurrentUserFailed(err));
        });
      }
    }).catchError((err) {
      eventBus.fire(new OnGetCurrentUserFailed(err));
    });
  }

  static void showMessageDialog(BuildContext context, dynamic error,
      {String? title, List<Widget>? actions, OnOk? onOk}) {
    String message = '';
    print('error $error');
    if (error is DioException) {
      final resp = error.response;
      if (resp != null) {
        if (resp.data['message'] != null) {
          message = resp.data['message'];
        } else if (resp.data['error'] != null) {
          if (resp.data['error'] is String) {
            message = resp.data['error'];
          } else if (resp.data['error']['errmsg'] != null) {
            message = resp.data['error']['errmsg'];
          } else if (resp.data['error']['message'] != null) {
            message = resp.data['error']['message'];
          } else {
            message = resp.data.toString();
          }
        }
      }
    } else if (error is String) {
      message = '$error';
    } else if (error.message != null) {
      message = error.message;
    } else {
      message = '$error';
    }
    showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text(title != null ? title : S.of(context).error),
            content: Text(message),
            actions: actions == null
                ? <Widget>[
                    TextButton(
                      child: Text(S.of(context).ok),
                      onPressed: onOk == null
                          ? () {
                              Navigator.of(context).pop();
                            }
                          : onOk,
                    ),
                  ]
                : actions,
          );
        });
  }

  static Future<List<int>> getLastVisit() async {
    Box box = await getBox();
    List<dynamic> lastVisit =
        json.decode(box.get(Constants.KEY_LAST_VISIT, defaultValue: '[]'));
    List<int> lv = lastVisit.map((e) => e as int).toList();
    store.dispatch(UpdateLastVisit(lv));
    return lv;
  }

  static void saveLastVisit(List<int> lastVisit) {
    getBox().then((box) {
      box.put(Constants.KEY_LAST_VISIT, lastVisit.toString());
    }).catchError((error) {
      print('Error occurred: $error');
    });
  }

  static String utcDatetimeStringToLocalDatetimeString(
      BuildContext context, String utcDatetimeString,
      {bool withTime = false}) {
    var utcDatetime;
    try {
      utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
          .parse(utcDatetimeString, true);
    } catch (error) {
      utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
          .parse(utcDatetimeString, true);
    }
    var localDatetime = utcDatetime.toLocal();
    return datetimeFormat(context, localDatetime, withTime: withTime);
  }

  static String datetimeFormat(BuildContext context, DateTime date,
      {bool withTime = false}) {
    final theDate = DateTime(date.year, date.month, date.day);
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    final yesterday = DateTime(now.year, now.month, now.day - 1);
    final tomorrow = DateTime(now.year, now.month, now.day + 1);
    DateFormat formatter = DateFormat('yyyy/MM/dd');
    DateFormat timeFormatter = DateFormat('HH:mm');
    if (withTime != null && withTime) {
      formatter = DateFormat('yyyy/MM/dd HH:mm:ss');
    }
    if (theDate == today) {
      return S.of(context).today_with_time(timeFormatter.format(date));
    }
    if (theDate == tomorrow) {
      return S.of(context).tomorrow_with_time(timeFormatter.format(date));
    }
    return formatter.format(date);
  }

  void uploadPicture(
      BuildContext context,
      File image,
      Map<String, dynamic> product,
      int imageId,
      String currentImageUrl,
      OnSuccess onSuccess,
      {int originalImageId = 0}) async {
    if (image != null && await image.exists()) {
      print('image: ${image.path}');
      String imageName = path.basename(image.path);
      MultipartFile imageFile =
          await MultipartFile.fromFile(image.path, filename: imageName);
      Map<String, dynamic> formMap = {
        'id': product['id'],
        'image': imageFile,
        'original_image_id': originalImageId
      };
      if (imageId == -1) {
        HttpUtil.httpPost(
          'upload-product-image/',
          (response) {
            if (onSuccess != null) {
              onSuccess(response);
            }
          },
          isFormData: true,
          body: formMap,
          sendProgress: (int sent, int total) {
            if (sent == total) {
              eventBus.fire(OnUploadImageProgressEvent(imageId, false, 1.0));
            } else {
              eventBus.fire(
                  OnUploadImageProgressEvent(imageId, true, sent / total));
            }
          },
        ).catchError((error) {
          Utils.showMessageDialog(context, error);
        });
      } else {
        HttpUtil.httpPost(
          'upload-product-gallery-image/',
          (response) {
            if (onSuccess != null) {
              onSuccess(response);
            }
          },
          isFormData: true,
          body: formMap,
          sendProgress: (int sent, int total) {
            if (sent == total) {
              eventBus.fire(OnUploadImageProgressEvent(imageId, false, 0.0));
            } else {
              eventBus.fire(
                  OnUploadImageProgressEvent(imageId, true, sent / total));
            }
          },
        ).catchError((error) {
          Utils.showMessageDialog(context, error);
        });
      }
    }
  }

  static CartInfo? getCartInfoByBusiness(
      List<CartInfo>? cartInfos, Business business) {
    if (cartInfos == null || cartInfos.length <= 0) {
      return null;
    }
    for (var i = 0; i < cartInfos.length; i++) {
      if (cartInfos[i].businessInfo?.id == business.id) {
        return cartInfos[i];
      }
    }
    return null;
  }

  static CartInfo? getCartInfoByBusinessId(
      List<CartInfo>? cartInfos, int businessId) {
    if (cartInfos == null || cartInfos.length <= 0) {
      return null;
    }
    for (var i = 0; i < cartInfos.length; i++) {
      if (cartInfos[i].businessInfo?.id == businessId) {
        return cartInfos[i];
      }
    }
    return null;
  }

  static List<CartInfo> addCartInfoToCartInfoList(
      List<CartInfo>? cartInfos, CartInfo cartInfo) {
    if (cartInfos == null || cartInfos.length <= 0) {
      cartInfos = [cartInfo];
    } else {
      bool found = false;
      for (var i = 0; i < cartInfos.length; i++) {
        if (cartInfos[i].businessInfo?.id == cartInfo.businessInfo?.id) {
          cartInfos[i] = cartInfo;
          found = true;
          break;
        }
      }
      if (!found) {
        cartInfos.add(cartInfo);
      }
    }
    Utils.getBox().then((box) {
      box.put(Constants.KEY_CARTINFOS, cartInfos.toString());
    }).catchError((error) {
      print('Error occurred: $error');
    });
    return cartInfos;
  }

  static List<CartInfo> removeCartInfoFromCartInfoList(
      List<CartInfo>? cartInfos, CartInfo? cartInfo) {
    if (cartInfos == null || cartInfos.length <= 0 || cartInfo == null) {
      return [];
    }
    int removeIndex = -1;
    for (var i = 0; i < cartInfos.length; i++) {
      if (cartInfo.businessInfo?.id == cartInfos[i].businessInfo?.id) {
        removeIndex = i;
        break;
      }
    }
    if (removeIndex != -1) {
      cartInfos.removeAt(removeIndex);
    }
    getBox().then((box) {
      box.put(Constants.KEY_CARTINFOS, cartInfos.toString());
    }).catchError((error) {
      print('Error occurred: $error');
    });
    return cartInfos;
  }

  static void clearCart(BuildContext context, CartInfo? cartInfo,
      {OnComplete? onComplete}) {
    if (cartInfo != null && cartInfo.productList != null && cartInfo.productList!.length > 0) {
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text(S.of(context).warning),
              content: Text(S.of(context).are_you_sure_to_empty_basket),
              actions: <Widget>[
                TextButton(
                  child: Text(S.of(context).cancel),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
                TextButton(
                  child: Text(S.of(context).yes_i_am_sure),
                  onPressed: () {
                    store.dispatch(new UpdateCartInfo(
                        Utils.removeCartInfoFromCartInfoList(
                            store.state.cartInfos, cartInfo)));
                    eventBus.fire(new OnCartInfoUpdated());
                    Navigator.of(context).pop();
                    if (onComplete != null) {
                      onComplete(null);
                    }
                  },
                )
              ],
            );
          });
    }
  }

  static void requireLogin(BuildContext context,
      {String? returnUrl, bool replace = true, int delayMilliseconds = 400}) {
    Future.delayed(Duration(milliseconds: delayMilliseconds), () {
      if (returnUrl != null) {
        store.dispatch(UpdateRedirectRoute(returnUrl));
      }
      Routes.router.navigateTo(context, '/login', replace: replace);
    });
  }

  static String getFirstNumberFromString(String numString) {
    final intInStr = RegExp(r'\d+');
    return intInStr.firstMatch(numString)?.group(0) ?? '';
  }

  static void loadProducts(int businessId, int categoryId, int page, bool more,
      OnComplete onComplete, OnError onError, {int featuredCount=4, int hotSaleCount=4, String keyword=''}) {
    print('feature $featuredCount');
    HttpUtil.httpGet(
      more ? 'v1/service-category-products-more' : 'v1/service-category-products',
      businessId: businessId,
      queryParameters: {
        // 'lat': store.state.position.latitude.toString(),
        // 'lng': store.state.position.longitude.toString(),
        'category_id': categoryId,
        'preview': Constants.isPreview,
        'page': page,
        'num_per_page': Constants.ORDERS_PER_PAGE,
        'featured_count': featuredCount,
        'hot_sale_count': hotSaleCount,
        'keyword': Uri.encodeComponent(keyword),
      },
    ).then((value) {
      if (onComplete != null) {
        onComplete(value);
      }
    }).catchError((error) {
      if (onError != null) {
        onError(error);
      }
    });
  }

  static String timestampToString(BuildContext context, int timestamp, {bool withTime=false}) {
    DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    if (timestamp == 0) {
      return '';
    }
    return datetimeFormat(context, date, withTime: withTime);
  }

  static String errorMsg2(BuildContext context, String code, String defaultString) {
    switch (code) {
      case 'no_delivery':
        return S.of(context).checkout_no_deliver;
      case 'no_shipping_address':
        return S.of(context).please_provide_shipping_address;
      case 'over_delivery_distance':
        return S.of(context).over_delivery_distance;
      case 'amount_not_meet':
        return S.of(context).amount_not_meet;
      case 'select_canada_post_shipping_rate':
        return S.of(context).select_canada_post_shipping_rate;
      case 'store_close':
        return S.of(context).store_closed;
    }
    return defaultString;
  }

  static String knownName(BuildContext context, String name) {
    switch (name) {
      case 'pickup-discount':
        return S.of(context).pickup_discount;
      case 'collect-tips':
        return S.of(context).service_fee;
      default:
        return name;
    }
  }

  static String getWeekDayName(BuildContext context, int timestamp, bool shortName) {
    DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    switch(date.weekday) {
      case 1:
        if (shortName) {
          return S.of(context).mon;
        } else {
          return S.of(context).monday;
        }
        break;
      case 2:
        if (shortName) {
          return S.of(context).tue;
        } else {
          return S.of(context).tuesday;
        }
        break;
      case 3:
        if (shortName) {
          return S.of(context).wed;
        } else {
          return S.of(context).wednesday;
        }
        break;
      case 4:
        if (shortName) {
          return S.of(context).thu;
        } else {
          return S.of(context).thursday;
        }
        break;
      case 5:
        if (shortName) {
          return S.of(context).fri;
        } else {
          return S.of(context).friday;
        }
        break;
      case 6:
        if (shortName) {
          return S.of(context).sat;
        } else {
          return S.of(context).saturday;
        }
        break;
      case 7:
        if (shortName) {
          return S.of(context).sun;
        } else {
          return S.of(context).sunday;
        }
        break;
      default:
        return '';
    }
    return '';
  }



  static int getProductLineInOrder(CartInfo cartInfo) {
    int qty = 0;
    for (CartLineItem lineItem in (cartInfo.productList ?? <CartLineItem>[])) {
      if (lineItem.product?.id != null) {
        qty += 1;
      }
    }
    return qty;
  }

  static void orderAgain(BuildContext context, CartInfo cartInfo) {
    cartInfo.productList?.removeWhere((element) => element.product == null || element.product?.id == null);
    store.dispatch(UpdateCartInfo(Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
    eventBus.fire(new OnCartInfoUpdated());
    Routes.router.navigateTo(context, '/checkout/${cartInfo.businessInfo?.id}');
  }

  static String getOrderStatus(BuildContext context, int statusCode) {
    switch(statusCode) {
      case Constants.STATUS_PENDING:
        return S.of(context).pending;
      case Constants.STATUS_ACCEPT:
        return S.of(context).order_acceipt;
      case Constants.STATUS_PROCESSING:
        return S.of(context).order_processing;
      case Constants.STATUS_COMPLETE:
        return S.of(context).order_complete;
      case Constants.STATUS_CANCELLED:
        return S.of(context).order_cancelled;
      default:
        return S.of(context).pending;
    }
  }

  static CartLineItem newCartLineItem(
      {int? id,
        double? price,
        Product? product,
        String? name,
        String? description,
        double? quantity,
        String? parentUuid,
      }) {
    CartLineItem lineItem = CartLineItem();
    lineItem.unitPrice = price;
    lineItem.product = product;
    lineItem.name = product?.name;
    lineItem.description = description;
    lineItem.quantity = quantity;
    lineItem.parentUuid = parentUuid;
    return lineItem;
  }

  static void addSubproductToCard(CartInfo cartInfo, CartLineItem lineItem) {
    final product = lineItem.product;
    final subs = product?.subproducts;
    if (subs == null || subs.isEmpty) return;
    for (int j = 0; j < subs.length; j++) {
      final sp = subs[j].product;
      cartInfo.productList?.add(newCartLineItem(
          id: 0,
          price: 0.0,
          product: Product(
              sp?.id ?? 0,
              product?.businessId ?? 0,
              sp?.name ?? '',
              sp?.price ?? 0.0,
              sp?.description ?? '',
              sp?.image ?? '',
              sp?.productAttributes ?? []
          ),
          name: sp?.name ?? '',
          description: sp?.description ?? '',
          quantity: 1.0,
          parentUuid: lineItem.uuid
      ));
    }
  }

  static void addSubproductQty(CartInfo cartInfo, CartLineItem lineItem,
      {bool remove = false}) {
    final product = lineItem.product;
    final subs = product?.subproducts;
    if (subs == null || subs.isEmpty) return;
    for (var i = 0; i < (cartInfo.productList?.length ?? 0); i++) {
      CartLineItem lineItem1 = cartInfo.productList![i];
      if (lineItem1.parentUuid == lineItem.uuid) {
        for (var j = 0; j < subs.length; j++) {
          if (subs[j].product?.id == lineItem1.product?.id) {
            if (remove) {
              lineItem1.quantity = (lineItem1.quantity ?? 0) - (subs[j].quantity ?? 0);
            } else {
              lineItem1.quantity = (lineItem1.quantity ?? 0) + (subs[j].quantity ?? 0);
            }
            break;
          }
        }
      }
    }
  }

  static void removeSubproduct(CartInfo cartInfo, String uuid) {
    cartInfo.productList?.removeWhere((element) => element.parentUuid == uuid);
  }

  static void openEmail(String email) async {
    final Uri params = Uri(
      scheme: 'mailto',
      path: '$email',
    );
    String url = params.toString();
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      print('Could not launch $url');
    }
  }

  static void callPhone(String phone) async {
    String callNo = 'tel://$phone';
    if (await canLaunch(callNo)) {
      await launch(callNo);
    } else {
      print('Could not call $phone');
    }
  }

  static Widget buildLine(String name, dynamic value, {double? nameSize, double? valueSize}) {
    Row row = Row(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [],
    );
    row.children.add(
      Container(
        width: 150,
        padding: EdgeInsets.only(),
        child: Text(
          name,
          style: TextStyle(
            color: Colors.black38,
            fontSize: nameSize,
          ),
        ),
      ),
    );
    if (value is String) {
      row.children.add(
        Expanded(
          child: Align(
            alignment: Alignment.centerRight,
            child: Text(
              value,
              style: TextStyle(
                fontSize: valueSize,
              ),
            ),
          ),
        ),
      );
    } else {
      row.children.add(
        Expanded(child: value),
      );
    }
    return Container(
      padding: EdgeInsets.only(top: 8, bottom: 8),
      child: row,
    );
  }

  static int getBusinessId() {
    return Constants.BUSINESS_ID;
  }
}

class RuntimeError extends Error {
  final int? code;
  final String message;
  RuntimeError(this.message, {this.code});
  String toString() => "Runtime Error: $message";
}
