import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:niogu_app/core/providers/app_provider.dart';
import 'package:niogu_app/features/goods/raw_materials/data/repositories/raw_material_repository_impl.dart';
import 'package:niogu_app/features/goods/raw_materials/domain/entities/raw_material.dart';
import 'package:niogu_app/features/goods/raw_materials/domain/repositories/i_raw_material_repository.dart';
import 'package:niogu_app/features/goods/raw_materials/presentation/controllers/raw_material_controller.dart';

final rawMaterialRepositoryProvider = Provider<IRawMaterialRepository>((ref) {
  final appDatabase = ref.watch(appDatabaseProvider);
  return RawMaterialRepositoryImpl(appDatabase);
});

final rawMaterialStreamProvider = StreamProvider.autoDispose<List<RawMaterial>>(
  (ref) {
    final rawMaterialRepository = ref.watch(rawMaterialRepositoryProvider);

    final currentOutlet = ref.watch(currentOutletIdProvider);
    
    if(currentOutlet == null) {
      return Stream<List<RawMaterial>>.value([]);
    }
    
    return rawMaterialRepository.watchMaterials(currentOutlet);
  },
);

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

final filteredRawMaterialProvider =
    Provider.autoDispose<AsyncValue<List<RawMaterial>>>((ref) {
      final rawMaterialStreamAsync = ref.watch(rawMaterialStreamProvider);
      final rawMaterialSearchAsync = ref.watch(rawMaterialSearchProvider);
      return rawMaterialStreamAsync.when(
        data: (rawMaterials) {
          if (rawMaterialSearchAsync.trim().isEmpty) {
            return AsyncValue.data(rawMaterials);
          }
          final filteredRawMaterials = rawMaterials.where((rawMaterial) {
            return rawMaterial.name.toLowerCase().contains(
              rawMaterialSearchAsync.toLowerCase(),
            );
          }).toList();

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

final rawMaterialEmptyProvider = Provider.autoDispose<RawMaterialEmpty>((ref) {
  final rawMaterialStreamAsync = ref.watch(rawMaterialStreamProvider);
  final filteredRawMaterialAsync = ref.watch(filteredRawMaterialProvider);
  final rawMaterialSearchAsync = ref.watch(rawMaterialSearchProvider);

  final allRawMaterials = rawMaterialStreamAsync.value ?? [];

  if (allRawMaterials.isEmpty) {
    return RawMaterialEmpty.empty_database;
  }

  final filteredRawMaterial = filteredRawMaterialAsync.value ?? [];

  if (rawMaterialSearchAsync.isNotEmpty && filteredRawMaterial.isEmpty) {
    return RawMaterialEmpty.empty_search_result;
  }

  return RawMaterialEmpty.has_data;
});

final productUsageMaterialStreamProvider = StreamProvider.family
    .autoDispose<List<ProductUsageMaterial>, bool>((ref, isEditing) {
      final rawMaterialRepository = ref.watch(rawMaterialRepositoryProvider);

      final currentOutlet = ref.watch(currentOutletIdProvider);
      
      if(currentOutlet == null) {
        return Stream<List<ProductUsageMaterial>>.value([]);
      }
      
      return rawMaterialRepository.watchProductUsageMaterials(
        currentOutlet,
        isEditing,
      );
    });

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

final filteredProductUsageMaterialProvider =
    Provider.family.autoDispose<AsyncValue<List<ProductUsageMaterial>>, bool>((ref,isEditing) {
      final productUsageMaterialStreamAsync = ref.watch(
        productUsageMaterialStreamProvider(isEditing),
      );
      final productUsageMaterialSearchAsync = ref.watch(
        productUsageMaterialSearchProvider,
      );

      return productUsageMaterialStreamAsync.when(
        data: (productUsageMaterials) {
          if (productUsageMaterialSearchAsync.trim().isEmpty) {
            return AsyncValue.data(productUsageMaterials);
          }

          final filteredProductUsageMaterials = productUsageMaterials.where((
            productUsageMaterial,
          ) {
            return productUsageMaterial.name.toLowerCase().contains(
              productUsageMaterialSearchAsync.toLowerCase(),
            );
          }).toList();

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

final productUsageMaterialEmptyProvider =
    Provider.family.autoDispose<ProductUsageRawMaterialEmpty, bool>((ref, isEditing) {
      final productUsageMaterialStreamAsync = ref.watch(
        productUsageMaterialStreamProvider(isEditing)
      );
      final filteredProductUsageMaterialAsync = ref.watch(
        filteredProductUsageMaterialProvider(isEditing)
      );
      final productUsageMaterialSearchAsync = ref.watch(
        productUsageMaterialSearchProvider,
      );

      final allProductUsageMaterials =
          productUsageMaterialStreamAsync.value ?? [];

      if (allProductUsageMaterials.isEmpty) {
        return ProductUsageRawMaterialEmpty.empty_database;
      }

      final filteredProductUsageMaterials =
          filteredProductUsageMaterialAsync.value ?? [];

      if (productUsageMaterialSearchAsync.isNotEmpty &&
          filteredProductUsageMaterials.isEmpty) {
        return ProductUsageRawMaterialEmpty.empty_search_result;
      }

      return ProductUsageRawMaterialEmpty.has_data;
    });

final rawMaterialControllerProvider =
    AsyncNotifierProvider.autoDispose<RawMaterialController, void>(
      RawMaterialController.new,
    );

final selectedProductUsageMaterialProvider =
    StateNotifierProvider.autoDispose<
      SelectedProductUsageMaterialController,
      Map<String, SelectedProductUsageMaterial>
    >((ref) {
      return SelectedProductUsageMaterialController();
    });

final receiptItemIdProvider = StateProvider<Map<String, String?>>((ref) => {});

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

final deleteProductUsageMaterialProvider =
    StateProvider<List<DeleteProductUsageMaterial>>((ref) => []);

final rawMaterialChoiceStreamProvider =
    StreamProvider.autoDispose<List<RawMaterialChoice>>((ref) {
      final rawMaterialRepository = ref.watch(rawMaterialRepositoryProvider);
      return rawMaterialRepository.watchRawMaterialChoices();
    });

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

final filteredRawMaterialChoiceProvider =
    Provider.autoDispose<AsyncValue<List<RawMaterialChoice>>>((ref) {
      final rawMaterialStreamAsync = ref.watch(rawMaterialChoiceStreamProvider);
      final rawMaterialSearchAsync = ref.watch(rawMaterialChoiceSearchProvider);
      return rawMaterialStreamAsync.when(
        data: (rawMaterials) {
          if (rawMaterialSearchAsync.trim().isEmpty) {
            return AsyncValue.data(rawMaterials);
          }
          final filteredRawMaterials = rawMaterials.where((rawMaterial) {
            return rawMaterial.name.toLowerCase().contains(
              rawMaterialSearchAsync.toLowerCase(),
            );
          }).toList();

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

final rawMaterialChoiceEmptyProvider =
    Provider.autoDispose<RawMaterialChoiceEmpty>((ref) {
      final rawMaterialStreamAsync = ref.watch(rawMaterialChoiceStreamProvider);
      final filteredRawMaterialAsync = ref.watch(
        filteredRawMaterialChoiceProvider,
      );
      final rawMaterialSearchAsync = ref.watch(rawMaterialChoiceSearchProvider);

      final allRawMaterials = rawMaterialStreamAsync.value ?? [];

      if (allRawMaterials.isEmpty) {
        return RawMaterialChoiceEmpty.empty_database;
      }

      final filteredRawMaterial = filteredRawMaterialAsync.value ?? [];

      if (rawMaterialSearchAsync.isNotEmpty && filteredRawMaterial.isEmpty) {
        return RawMaterialChoiceEmpty.empty_search_result;
      }

      return RawMaterialChoiceEmpty.has_data;
    });
