import 'dart:convert';

import '../../../common/config.dart';
import '../../../common/constants.dart';
import '../../../modules/dynamic_layout/helper/helper.dart';

class ProductQuery {
  String? category;
  String? tag;
  bool? featured;
  bool? onSale;
  num? maxPrice;
  num? minPrice;
  String? orderBy;
  String? order;
  int? perPage;
  int? page;
  String? include;
  String? search;
  String? sku;
  Map? advancedParams;
  String? userId;
  List<String>? brandIds;
  Map? attributes;
  String? stockStatus = kAdvanceConfig.hideOutOfStock
      ? 'instock' // If add this param to API, the result will not contain backorder products
      : null;
  List<String>? exclude = kExcludedProductIDs?.split(',');

  ProductQuery({
    this.category,
    this.tag,
    this.featured,
    this.onSale,
    this.maxPrice,
    this.minPrice,
    this.orderBy,
    this.order,
    this.perPage,
    this.page,
    this.include,
    this.search,
    this.sku,
    this.advancedParams,
    this.userId,
    this.brandIds,
    this.attributes,
    this.stockStatus,
    this.exclude,
  });

  /// Used for object initialization that does not
  /// specify the data type of the value components
  ProductQuery.parse({
    dynamic category,
    dynamic tag,
    dynamic featured,
    dynamic onSale,
    dynamic maxPrice,
    dynamic minPrice,
    dynamic orderBy,
    dynamic order,
    dynamic perPage,
    dynamic page,
    dynamic include,
    dynamic search,
    dynamic sku,
    dynamic advancedParams,
    dynamic userId,
    dynamic brandIds,
    dynamic attributes,
    dynamic stockStatus,
    dynamic exclude,
  }) {
    this.category = category != null ? '$category' : this.category;
    this.tag = tag != null ? '$tag' : this.tag;
    this.featured = bool.tryParse('$featured') ?? this.featured;
    this.onSale = bool.tryParse('$onSale') ?? this.onSale;
    this.maxPrice = num.tryParse('$maxPrice') ?? this.maxPrice;
    this.minPrice = num.tryParse('$minPrice') ?? this.minPrice;
    this.orderBy = orderBy is String ? orderBy : this.orderBy;
    this.order = order is String ? order : this.order;
    this.perPage = int.tryParse('$perPage') ?? this.perPage;
    this.page = int.tryParse('$page') ?? this.page;
    this.include = include is String ? include : this.include;
    this.search = search is String ? search : this.search;
    this.sku = sku is String ? sku : this.sku;
    this.advancedParams = advancedParams is Map
        ? advancedParams
        : this.advancedParams;
    this.brandIds = brandIds is List<String> ? brandIds : this.brandIds;
    this.userId = userId != null ? '$userId' : this.userId;
    this.attributes = attributes is Map ? attributes : this.attributes;
    this.stockStatus = stockStatus is String ? stockStatus : this.stockStatus;
    this.exclude = exclude is List<String> ? exclude : this.exclude;
  }

  ProductQuery.fromJson(dynamic json) {
    final category = json['category'];
    if (category is List) {
      this.category = category.join(',');
    } else if (category != null) {
      this.category = '${json['category']}';
    }
    tag = json['tag'] != null ? '${json['tag']}' : tag;
    featured = bool.tryParse('${json['featured']}') ?? featured;
    onSale = bool.tryParse('${json['onSale']}') ?? onSale;
    maxPrice = num.tryParse('${json['maxPrice']}') ?? maxPrice;
    minPrice = num.tryParse('${json['minPrice']}') ?? minPrice;
    orderBy = json['orderby'] is String ? json['orderby'] : orderBy;
    order = json['order'] is String ? json['order'] : order;
    perPage = int.tryParse('${json['limit']}') ?? perPage;
    page = int.tryParse('${json['page']}') ?? page;
    include = json['include'] is String
        ? json['include']
        : json['include'] is List
        ? json['include'].join(',')
        : include;
    search = json['search'] is String ? json['search'] : search;
    sku = json['sku'] is String ? json['sku'] : sku;
    advancedParams = json['advancedParams'] is Map
        ? json['advancedParams']
        : advancedParams;
    brandIds = json['brandIds'] is List<String> ? json['brandIds'] : brandIds;
    userId = json['userId'] != null ? '${json['userId']}' : userId;
    attributes = json['attributes'] is Map
        ? json['attributes']
        : (json['attribute'] != null || json['attributeId'] != null)
        ? {json['attribute']: json['attributeId']}
        : attributes;
    stockStatus = json['stockStatus'] is String
        ? json['stockStatus']
        : stockStatus;
    exclude = json['exclude'] is List<String> ? json['exclude'] : exclude;
  }

  String toParams({int apiVersion = 2}) {
    var params = '';
    if (advancedParams?.isNotEmpty ?? false) {
      advancedParams?.forEach((key, value) {
        params += '&$key';
        if (value?.toString().isNotEmpty ?? false) {
          params += '=$value';
        }
      });
    }

    /// Add category filter
    if ((category?.isNotEmpty ?? false) && category != kEmptyCategoryID) {
      params += '&category=$category';
    }
    if (tag?.isNotEmpty ?? false) {
      params += '&tag=$tag';
    }

    if (brandIds?.isNotEmpty ?? false) {
      params += '&brand=${brandIds!.join(',')}';
    }

    /// Add featured filter
    if (featured == true) {
      params += '&featured=$featured';
    }

    /// Add onSale filter
    if (onSale == true) {
      params += '&on_sale=$onSale';
    }

    /// Add range price filter
    if (minPrice != null && maxPrice != null && (maxPrice ?? 0) > 0) {
      params +=
          '&min_price=${minPrice?.toInt()}&max_price=${maxPrice?.toInt()}';
    }

    /// Add attribute filter
    if (attributes?.isNotEmpty ?? false) {
      // If there is only one attribute, use `attribute` and `attribute_term`
      // for compatibility with woocommerce rest api
      if (attributes!.length == 1) {
        final attribute = attributes!.entries.first;
        final attributeId = attribute.key?.toString();
        final attributeTerm = attribute.value?.toString();

        if (attributeId?.isNotEmpty ?? false) {
          params += '&attribute=$attributeId';
        }
        if (attributeTerm?.isNotEmpty ?? false) {
          params += '&attribute_term=$attributeTerm';
        }
      } else {
        params += '&attributes=${jsonEncode(attributes)}';
      }
    }

    /// Sort by
    if (orderBy?.isNotEmpty ?? false) {
      params += '&orderby=$orderBy';
    }
    if (order?.isNotEmpty ?? false) {
      params += '&order=$order';
    }

    /// Page & limit
    if (page != null) {
      params += '&page=$page';
    }
    params += '&per_page=${perPage ?? apiPageSize}';

    /// Add user filter
    if (userId?.isNotEmpty ?? false) {
      params += '&user_id=$userId';
    }

    /// Only load special products
    if (include?.isNotEmpty ?? false) {
      var result = <int>[];
      var ids = include?.split(',') ?? [];
      for (var id in ids) {
        var idProduct = Helper.formatInt(id);
        if (idProduct != null) {
          result.add(idProduct);
        }
      }
      params += '&include=${result.toList().join(',')}';
    }

    /// Search product
    if (search?.isNotEmpty ?? false) {
      params += '&search=$search';
    }

    // The `sku` parameter will limit result set to products with a specific
    // SKU. Does not work properly if used with the `search` parameter.
    //
    // In the new version of WooCommerce API V3, they have provided the param
    // `search_sku`. Do a partial match for a sku. Supercedes sku parameter that
    // does exact matching.
    if (sku?.isNotEmpty ?? false) {
      // TODO: Remove function [_searchBySku] on the class [WooCommerceService] if apply
      params += '&sku=$sku';

      // if (apiVersion < 3) {
      //   params += '&sku=$sku';
      // } else {
      //   params += '&search_sku=$sku';
      // }
    }

    if (stockStatus?.isNotEmpty ?? false) {
      params += '&stock_status=$stockStatus';
    }

    if (exclude?.isNotEmpty ?? false) {
      params += '&exclude=${exclude!.join(',')}';
    }
    return params;
  }
}
