import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:niogu_app/core/entities/custom_activity.dart';
import 'package:niogu_app/core/providers/app_provider.dart';
import 'package:niogu_app/core/enums/report_type.dart';
import 'package:niogu_app/features/goods/products/presentation/providers/category_provider.dart';
import 'package:niogu_app/features/online_store/data/repositories/online_store_repository_impl.dart';
import 'package:niogu_app/features/online_store/domain/entities/online_store.dart';
import 'package:niogu_app/features/online_store/domain/repositories/i_online_store_repository.dart';
import 'package:niogu_app/features/online_store/presentation/controllers/online_store_controller.dart';
import 'package:niogu_app/features/report/presentation/providers/report_provider.dart';

final onlineStoreRepositoryProvider =
    Provider.autoDispose<IOnlineStoreRepository>((ref) {
      final appDatabase = ref.watch(appDatabaseProvider);
      return OnlineStoreRepositoryImpl(appDatabase);
    });

final onlineStoreProductStreamProvider =
    StreamProvider.autoDispose<List<OnlineStoreProduct>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchOnlineStoreProducts();
    });

final onlineStoreProductSearchProvider = StateProvider.autoDispose<String>(
  (ref) => '',
);

final filteredOnlineStoreProductProvider =
    Provider.autoDispose<AsyncValue<List<OnlineStoreProduct>>>((ref) {
      final onlineStoreProductStreamAsync = ref.watch(
        onlineStoreProductStreamProvider,
      );

      final onlineStoreProductSearchAsync = ref.watch(
        onlineStoreProductSearchProvider,
      );

      return onlineStoreProductStreamAsync.when(
        data: (products) {
          if (onlineStoreProductSearchAsync.trim().isEmpty) {
            return AsyncValue.data(products);
          }

          final filteredOnlineStoreProducts = products.where((product) {
            return product.name.toLowerCase().contains(
              onlineStoreProductSearchAsync,
            );
          }).toList();

          return AsyncValue.data(filteredOnlineStoreProducts);
        },
        error: (error, stackTrace) => AsyncValue.error(error, stackTrace),
        loading: () => const AsyncValue.loading(),
      );
    });

final onlineStoreProductEmptyProvider =
    Provider.autoDispose<OnlineStoreProductEmpty>((ref) {
      final onlineStoreProductStreamAsync = ref.watch(
        onlineStoreProductStreamProvider,
      );

      final filteredOnlineStoreProductAsync = ref.watch(
        filteredOnlineStoreProductProvider,
      );

      final onlineStoreProductSearchAsync = ref.watch(
        onlineStoreProductSearchProvider,
      );

      final allProductOnlineStores = onlineStoreProductStreamAsync.value ?? [];

      if (allProductOnlineStores.isEmpty) {
        return OnlineStoreProductEmpty.empty_database;
      }

      final filteredOnlineStoreProducts =
          filteredOnlineStoreProductAsync.value ?? [];

      if (onlineStoreProductSearchAsync.isNotEmpty &&
          filteredOnlineStoreProducts.isEmpty) {
        return OnlineStoreProductEmpty.empty_search_result;
      }

      return OnlineStoreProductEmpty.has_data;
    });

final onlineStoreControllerProvider =
    AsyncNotifierProvider.autoDispose<OnlineStoreController, void>(
      OnlineStoreController.new,
    );

final selectedOnlineStoreProductProvider =
    StateProvider.autoDispose<Map<String, String>>((ref) => {});

final expandedOnlineStoreProductProvider = StateProvider.family
    .autoDispose<bool, String>((ref, productId) => false);

final onlineOrderHasNotReviewStreamProvider =
    StreamProvider.autoDispose<MessageActivity?>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchOrderOnlineHasNotReview();
    });

final numberOfNeedToBeProcessedStreamProvider = StreamProvider.autoDispose<int>(
  (ref) {
    final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
    return onlineStoreRepository.watchNumberOfNeedToBeProcessed();
  },
);

final onlineOrderStreamProvider = StreamProvider.autoDispose<List<OnlineOrder>>(
  (ref) {
    final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);

    final outletId = ref.watch(
      filteredByOutletProvider(ReportType.online_order),
    );

    final dateRange = ref.watch(
      filteredByDateTimeProvider(ReportType.online_order),
    );

    final startDate = dateRange?.start;

    final endDate = dateRange?.end;

    return onlineStoreRepository.watchOnlineOrders(
      outletId: outletId,
      startDate: startDate,
      endDate: endDate,
    );
  },
);

final onlineOrderSearchProvider = StateProvider.autoDispose<String>(
  (ref) => '',
);

final filteredOnlineOrderProvider =
    Provider.autoDispose<AsyncValue<List<OnlineOrder>>>((ref) {
      final onlineOrderStreamAsync = ref.watch(onlineOrderStreamProvider);

      final onlineOrderSearchAsync = ref.watch(onlineOrderSearchProvider);

      return onlineOrderStreamAsync.when(
        data: (orders) {
          if (onlineOrderSearchAsync.trim().isEmpty) {
            return AsyncValue.data(orders);
          }

          final filteredOrders = orders.where((order) {
            return order.orderNumber.toLowerCase().contains(
              onlineOrderSearchAsync.toLowerCase(),
            );
          }).toList();

          return AsyncValue.data(filteredOrders);
        },
        error: (error, stackTrace) {
          return AsyncValue.error(error, stackTrace);
        },
        loading: () => const AsyncValue.loading(),
      );
    });

final onlineOrderEmptyProvider = Provider.autoDispose<OnlineOrderEmpty>((ref) {
  final onlineOrderStreamAsync = ref.watch(onlineOrderStreamProvider);

  final filteredOnlineOrderAsync = ref.watch(filteredOnlineOrderProvider);

  final onlineOrderSearchAsync = ref.watch(onlineOrderSearchProvider);

  if (onlineOrderStreamAsync.isLoading) {
    return OnlineOrderEmpty.loading;
  }

  final allOrderOnline = onlineOrderStreamAsync.value ?? [];

  if (allOrderOnline.isEmpty) {
    return OnlineOrderEmpty.empty_database;
  }

  final filteredOnlineOrder = filteredOnlineOrderAsync.value ?? [];

  if (onlineOrderSearchAsync.isNotEmpty && filteredOnlineOrder.isEmpty) {
    return OnlineOrderEmpty.empty_search_result;
  }

  return OnlineOrderEmpty.has_data;
});

final orderStatusByIdProvider = StreamProvider.family
    .autoDispose<ViewOrderStatus, String>((ref, orderId) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);

      return onlineStoreRepository.watchOrderStatusById(orderId);
    });

final displayProductReviewStreamProvider =
    StreamProvider.autoDispose<List<DisplayProductReview>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchProductReviews();
    });

final reviewDetailStreamProvider = StreamProvider.family
    .autoDispose<DetailProductReview, String>((ref, productId) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchDetailProductReview(productId);
    });

final productCommentStreamProvider = StreamProvider.family
    .autoDispose<List<ProductComment>, String>((ref, productId) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchProductComments(productId);
    });

final campaignStreamProvider =
    StreamProvider.autoDispose<List<DisplayCampaign>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchCampaigns();
    });

final productCampaignStreamProvider =
    StreamProvider.autoDispose<List<ProductCampaign>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchProductCampaigns();
    });

final productCampaignSearchProvider = StateProvider.autoDispose<String>(
  (ref) => '',
);

final filteredProductCampaignStreamProvider =
    Provider.autoDispose<AsyncValue<List<ProductCampaign>>>((ref) {
      final productStreamAsync = ref.watch(productCampaignStreamProvider);

      final productSearchAsync = ref.watch(productCampaignSearchProvider);

      return productStreamAsync.when(
        data: (products) {
          if (productSearchAsync.trim().isEmpty) {
            return AsyncValue.data(products);
          }

          final filteredProducts = products.where((product) {
            return product.name.toLowerCase().contains(
              productSearchAsync.toLowerCase(),
            );
          }).toList();

          return AsyncValue.data(filteredProducts);
        },
        error: (error, stackTrace) => AsyncValue.error(error, stackTrace),
        loading: () => const AsyncValue.loading(),
      );
    });

final productCampaignEmptyProvider = Provider.autoDispose<ProductCampaignEmpty>(
  (ref) {
    final productStreamAsync = ref.watch(productCampaignStreamProvider);

    final filteredProductAsync = ref.watch(
      filteredProductCampaignStreamProvider,
    );

    final productSearchAsync = ref.watch(productCampaignSearchProvider);

    if (productStreamAsync.isLoading) {
      return ProductCampaignEmpty.loading;
    }

    final allProducts = productStreamAsync.value ?? [];

    if (allProducts.isEmpty) {
      return ProductCampaignEmpty.empty_database;
    }

    final filteredProducts = filteredProductAsync.value ?? [];

    if (productSearchAsync.isNotEmpty && filteredProducts.isEmpty) {
      return ProductCampaignEmpty.empty_search_result;
    }

    return ProductCampaignEmpty.has_data;
  },
);

final categoryCampaignStreamProvider =
    StreamProvider.autoDispose<List<CategoryCampaign>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchCategoryCampaigns();
    });

final categoryCampaignSearchProvider = StateProvider.autoDispose((ref) => '');

final filteredCategoryCampaignStreamProvider =
    Provider.autoDispose<AsyncValue<List<CategoryCampaign>>>((ref) {
      final categoryStreamAsync = ref.watch(categoryCampaignStreamProvider);

      final categorySearchAsync = ref.watch(categoryCampaignSearchProvider);

      return categoryStreamAsync.when(
        data: (categories) {
          if (categorySearchAsync.trim().isEmpty) {
            return AsyncValue.data(categories);
          }

          final filteredCategories = categories.where((category) {
            return category.name.toLowerCase().contains(
              categorySearchAsync.toLowerCase(),
            );
          }).toList();

          return AsyncValue.data(filteredCategories);
        },
        error: (error, stackTrace) => AsyncValue.error(error, stackTrace),
        loading: () => const AsyncValue.loading(),
      );
    });

final categoryCampaignEmptyProvider =
    Provider.autoDispose<CategoryCampaignEmpty>((ref) {
      final categoryStreamAsync = ref.watch(categoryStreamProvider);

      final filteredCategoryStreamAsync = ref.watch(
        filteredCategoryCampaignStreamProvider,
      );

      final categorySearchAsync = ref.watch(categorySearchProvider);

      if (categoryStreamAsync.isLoading) {
        return CategoryCampaignEmpty.loading;
      }

      final allCategories = categoryStreamAsync.value ?? [];

      if (allCategories.isEmpty) {
        return CategoryCampaignEmpty.empty_database;
      }

      final filteredCategories = filteredCategoryStreamAsync.value ?? [];

      if (categorySearchAsync.isNotEmpty && filteredCategories.isEmpty) {
        return CategoryCampaignEmpty.empty_search_result;
      }

      return CategoryCampaignEmpty.has_data;
    });

final selectedCampaignItemProvider =
    StateProvider.autoDispose<SelectedItemCampaign?>((ref) => null);

final targetOutletStreamProvider =
    StreamProvider.autoDispose<List<TargetOutlet>>((ref) {
      final onlineStoreRepository = ref.watch(onlineStoreRepositoryProvider);
      return onlineStoreRepository.watchTargetOutlets();
    });

final selectedTargetOutletControllerProvider =
    StateNotifierProvider.autoDispose<
      SelectedTargetOutletController,
      Map<String, SelectedTargetOutlet>
    >((ref) {
      return SelectedTargetOutletController();
    });
