import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flux_localization/flux_localization.dart';
import 'package:flux_ui/flux_ui.dart';
import 'package:inspireui/extensions/color_extension.dart';
import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';

import '../../../common/config.dart';
import '../../../models/category/list_product_model.dart';
import '../../../models/entities/category.dart';
import '../../../models/index.dart' show CategoryModel, ProductModel;
import '../../../services/index.dart';
import '../category/category_icon_gradient_item.dart';
import '../category/category_icon_item.dart';
import '../config/category_config.dart';
import '../config/category_item_config.dart';
import '../config/product_config.dart';
import '../helper/helper.dart';

class MenuLayoutWithCustomCategory extends StatefulWidget {
  final ProductConfig config;
  final CategoryConfig categoryConfig;

  const MenuLayoutWithCustomCategory({
    required this.config,
    required this.categoryConfig,
  });

  @override
  State<MenuLayoutWithCustomCategory> createState() => _StateMenuLayout();
}

class _StateMenuLayout extends State<MenuLayoutWithCustomCategory> {
  final _productCache = <String, ListProductModel>{};

  final _positionNotifier = ValueNotifier(0);

  int get position => _positionNotifier.value;

  set position(int value) => _positionNotifier.value = value;

  CategoryConfig get categoryConfig => widget.categoryConfig;

  @override
  void dispose() {
    _positionNotifier.dispose();
    _productCache.forEach((key, value) {
      value.dispose();
    });
    super.dispose();
  }

  List<Category> matchCategories(
    List<Category>? categories,
    List<CategoryItemConfig> items,
  ) {
    final values = categories ?? [];
    final categoryItems = values.where((item) {
      return items.indexWhere((e) => item.id == e.category) != -1;
    }).toList();

    final results = <Category>[];

    // Sort the results by the order of the items
    for (var item in items) {
      final category = categoryItems.firstWhereOrNull(
        (element) => element.id == item.category,
      );
      if (category != null) {
        results.add(category);
      }
    }
    return results;
  }

  void onTapSeeAll(List<Category> categories) {
    if (categories.length <= position) {
      return;
    }
    var item = categories[position];
    ProductModel.showList(
      config: widget.config.jsonData,
      cateId: item.id,
      context: context,
    );
  }

  Widget renderMenuHeader(bool isFirstLoad, List<Category> categories) {
    return SliverToBoxAdapter(
      child: BackgroundColorWidget(
        margin: EdgeInsets.only(
          left: categoryConfig.marginLeft,
          right: categoryConfig.marginRight,
          top: categoryConfig.marginTop,
          bottom: categoryConfig.marginBottom,
        ),
        enable: categoryConfig.enableBackground ?? true,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(6.0),
          boxShadow: [
            if (categoryConfig.shadow != null)
              BoxShadow(
                color: Colors.black.withValueOpacity(0.1),
                blurRadius: Helper.formatDouble(categoryConfig.shadow ?? 15.0)!,
                offset: Offset(
                  0,
                  Helper.formatDouble(categoryConfig.shadow ?? 10.0)!,
                ),
              ),
          ],
        ),
        child: SizedBox(
          height: 100,
          child: Builder(
            builder: (_) {
              if (isFirstLoad) return kLoadingWidget(context);
              if (categories.isEmpty) return const SizedBox();
              return ValueListenableBuilder(
                valueListenable: _positionNotifier,
                builder: (context, value, _) {
                  return ListView.separated(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    separatorBuilder: (context, index) =>
                        SizedBox(width: categoryConfig.separateWidth),
                    scrollDirection: Axis.horizontal,
                    itemCount: categories.length,
                    itemBuilder: (context, index) {
                      final item = categories[index];
                      final items = categoryConfig.items;
                      final itemConfig = items.firstWhereOrNull(
                        (e) => e.category == item.id,
                      );

                      if (categoryConfig.gradientDesign) {
                        return CategoryIconGradientItem(
                          isSelected: index == value,
                          commonConfig: categoryConfig.commonItemConfig,
                          itemConfig: itemConfig,
                          isHorizontal: categoryConfig.horizontalItem,
                          isWrap: categoryConfig.wrap,
                          iconSize: 64,
                          onTap: (_) => position = index,
                        );
                      }

                      return CategoryIconItem(
                        isSelected: index == value,
                        commonConfig: categoryConfig.commonItemConfig,
                        itemConfig: itemConfig,
                        isHorizontal: categoryConfig.horizontalItem,
                        isWrap: categoryConfig.wrap,
                        iconSize: 64,
                        onTap: (_) => position = index,
                      );
                    },
                  );
                },
              );
            },
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final categoryConfig = widget.categoryConfig;

    return LayoutBuilder(
      builder: (_, constraints) {
        final isTablet = Layout.isDisplayTablet(context);

        var widthScreen = constraints.maxWidth;

        /// 2 columns on mobile, 3 columns on ipad
        var crossAxisCount = isTablet ? 3 : 2;
        var widthContent = isTablet ? widthScreen / 3 : (widthScreen / 2);

        var itemHeight = widget.config.productListItemHeight;
        if (widget.config.showQuantity) {
          itemHeight += 30;
        }
        if (widget.config.showCartButton) {
          itemHeight += 30;
        }

        var childAspectRatio =
            (isTablet ? 0.94 : 1) *
            widthContent /
            (widthContent * (widget.config.imageRatio) + itemHeight);

        return BackgroundColorWidget(
          enable: widget.config.enableBackground,
          child: LimitedBox(
            maxHeight: constraints.maxHeight,
            child: Selector<CategoryModel, bool>(
              selector: (context, model) => model.isLoading,
              builder: (_, isLoading, __) {
                final model = context.read<CategoryModel>();
                final items = categoryConfig.items;
                final categories = matchCategories(model.categories, items);
                return CustomScrollView(
                  cacheExtent: 1000,
                  slivers: <Widget>[
                    if (widget.config.name != null)
                      SliverToBoxAdapter(
                        child: HeaderView(
                          headerText: widget.config.name ?? '',
                          showSeeAll: widget.config.showSeeAll,
                          callback: () => onTapSeeAll(categories),
                        ),
                      ),
                    renderMenuHeader(model.isFirstLoad, categories),
                    if (categories.isNotEmpty)
                      ValueListenableBuilder(
                        valueListenable: _positionNotifier,
                        builder: (context, position, child) {
                          const emptyWidget = SliverToBoxAdapter(
                            child: SizedBox(),
                          );
                          if (categories.length <= position) {
                            return emptyWidget;
                          }
                          final item = categories[position];
                          final id = item.id;
                          if (id == null) return emptyWidget;
                          return SliverListProductItemWidget(
                            key: ValueKey(id),
                            model: _productCache[id],
                            categoryId: id,
                            putIfAbsent: (value) => _productCache[id] = value,
                            crossAxisCount: crossAxisCount,
                            childAspectRatio: childAspectRatio,
                            widthContent: widthContent,
                            config: widget.config,
                          );
                        },
                      ),
                  ],
                );
              },
            ),
          ),
        );
      },
    );
  }
}

class SliverListProductItemWidget extends StatefulWidget {
  final ListProductModel? model;
  final String categoryId;
  final Function(ListProductModel) putIfAbsent;
  final int crossAxisCount;
  final double childAspectRatio;
  final double widthContent;
  final ProductConfig config;

  const SliverListProductItemWidget({
    this.model,
    required this.categoryId,
    required this.putIfAbsent,
    required this.crossAxisCount,
    required this.childAspectRatio,
    required this.widthContent,
    required this.config,
    super.key,
  });

  @override
  StateSliverListProductItemWidget createState() =>
      StateSliverListProductItemWidget();
}

class StateSliverListProductItemWidget
    extends State<SliverListProductItemWidget> {
  /// limit products
  int get limit => 25;

  late ListProductModel model;

  SliverMainAxisGroup _buildProductGroup({
    required Widget sliverProducts,
    required Widget loadMoreSliver,
  }) {
    return SliverMainAxisGroup(slivers: [sliverProducts, loadMoreSliver]);
  }

  Widget _buildLoadMoreSliver(ListProductModel model) {
    return SliverToBoxAdapter(
      child: model.hasNext
          ? VisibilityDetector(
              key: const Key('loading_vertical'),
              onVisibilityChanged: (VisibilityInfo info) {
                if (info.visibleFraction > 0.1) {
                  model.getData();
                }
              },
              child: Center(child: Text(S.of(context).loading)),
            )
          : Center(child: Text(S.of(context).noData)),
    );
  }

  @override
  void initState() {
    super.initState();
    model =
        widget.model ??
        ListProductModel(categoryId: widget.categoryId, limit: limit);
    if (widget.model == null) {
      widget.putIfAbsent(model);
      model.getData();
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: model,
      builder: (context, child) {
        if (model.isFirstLoad) {
          return SliverToBoxAdapter(child: kLoadingWidget(context));
        }

        final products = model.data;

        if (products.isEmpty) {
          return SliverToBoxAdapter(
            child: Container(
              height: 100,
              alignment: Alignment.center,
              child: Text(S.of(context).noProduct),
            ),
          );
        }

        if (widget.config.cardDesign == CardDesign.horizontal) {
          return _buildProductGroup(
            sliverProducts: SliverList(
              delegate: SliverChildBuilderDelegate(
                addAutomaticKeepAlives: false,
                (BuildContext context, int index) {
                  return Services().widget.renderProductCardView(
                    item: products[index],
                    width: widget.widthContent,
                    config: widget.config,
                    ratioProductImage: widget.config.imageRatio,
                  );
                },
                childCount: products.length,
              ),
            ),
            loadMoreSliver: _buildLoadMoreSliver(model),
          );
        }

        return _buildProductGroup(
          sliverProducts: SliverPadding(
            padding: EdgeInsets.symmetric(
              horizontal: widget.config.hPadding,
              vertical: widget.config.vPadding,
            ),
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: widget.crossAxisCount,
                crossAxisSpacing: 0.0,
                mainAxisSpacing: 0.0,
                childAspectRatio: widget.childAspectRatio,
              ),
              delegate: SliverChildBuilderDelegate(
                addAutomaticKeepAlives: false,
                (BuildContext context, int index) {
                  return Services().widget.renderProductCardView(
                    item: products[index],
                    width: widget.widthContent,
                    config: widget.config,
                    ratioProductImage: widget.config.imageRatio,
                  );
                },
                childCount: products.length,
              ),
            ),
          ),
          loadMoreSliver: _buildLoadMoreSliver(model),
        );
      },
    );
  }
}
