/// Provider: Products Provider
///
/// Manages the state of products data using Riverpod.
/// Provides filtered products based on category and search query.
/// Fetches data from Frappe ERPNext API via remote data source.
library;

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/core/services/frappe_auth_provider.dart';
import 'package:worker/features/products/data/datasources/products_local_datasource.dart';
import 'package:worker/features/products/data/datasources/products_remote_datasource.dart';
import 'package:worker/features/products/data/repositories/products_repository_impl.dart';
import 'package:worker/features/products/domain/entities/product.dart';
import 'package:worker/features/products/domain/repositories/products_repository.dart';
import 'package:worker/features/products/domain/usecases/get_products.dart';
import 'package:worker/features/products/domain/usecases/search_products.dart';
import 'package:worker/features/products/domain/usecases/get_product_detail.dart';
import 'package:worker/features/products/presentation/providers/selected_brand_provider.dart';
import 'package:worker/features/products/presentation/providers/search_query_provider.dart';

part 'products_provider.g.dart';

/// Products Local DataSource Provider
@riverpod
ProductsLocalDataSource productsLocalDataSource(Ref ref) {
  return const ProductsLocalDataSourceImpl();
}

/// Products Remote DataSource Provider
@riverpod
Future<ProductsRemoteDataSource> productsRemoteDataSource(Ref ref) async {
  final dioClient = await ref.watch(dioClientProvider.future);
  final frappeAuthService = ref.watch(frappeAuthServiceProvider);
  return ProductsRemoteDataSource(dioClient, frappeAuthService);
}

/// Products Repository Provider
@riverpod
Future<ProductsRepository> productsRepository(Ref ref) async {
  final localDataSource = ref.watch(productsLocalDataSourceProvider);
  final remoteDataSource = await ref.watch(productsRemoteDataSourceProvider.future);
  return ProductsRepositoryImpl(
    localDataSource: localDataSource,
    remoteDataSource: remoteDataSource,
  );
}

/// Products Provider
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API with pagination support.
///
/// Usage:
/// ```dart
/// final productsAsync = ref.watch(productsProvider);
///
/// productsAsync.when(
///   data: (products) => ProductGrid(products: products),
///   loading: () => CircularProgressIndicator(),
///   error: (error, stack) => ErrorWidget(error),
/// );
/// ```
@riverpod
class Products extends _$Products {
  static const int pageSize = 12;
  int _currentPage = 0;
  bool _hasMore = true;

  @override
  Future<List<Product>> build() async {
    // Reset pagination when dependencies change
    _currentPage = 0;
    _hasMore = true;

    // Watch dependencies
    final selectedBrand = ref.watch(selectedBrandProvider);
    final searchQuery = ref.watch(searchQueryProvider);

    // Get repository with injected data sources
    final repository = await ref.watch(productsRepositoryProvider.future);

    // Fetch first page of products
    List<Product> products;

    if (searchQuery.isNotEmpty) {
      // Search takes precedence over brand filter
      final searchUseCase = SearchProducts(repository);
      products = await searchUseCase(searchQuery);

      // If a brand is selected, filter search results by brand
      if (selectedBrand != 'all') {
        products = products
            .where((product) => product.brand == selectedBrand)
            .toList();
      }

      // For search, we fetch all results at once, so no more pages
      _hasMore = false;
    } else {
      // No search query, fetch all products with pagination
      final getProductsUseCase = GetProducts(repository);
      products = await getProductsUseCase(
        limitStart: 0,
        limitPageLength: pageSize,
      );

      // Filter by brand if not 'all'
      if (selectedBrand != 'all') {
        products = products
            .where((product) => product.brand == selectedBrand)
            .toList();
      }

      // If we got less than pageSize, there are no more products
      _hasMore = products.length >= pageSize;
    }

    _currentPage = 1;
    return products;
  }

  /// Load more products (next page)
  Future<void> loadMore() async {
    if (!_hasMore) return;

    // Watch dependencies to get current filters
    final selectedBrand = ref.read(selectedBrandProvider);
    final searchQuery = ref.read(searchQueryProvider);

    // Don't paginate search results (already fetched all)
    if (searchQuery.isNotEmpty) return;

    // Get repository
    final repository = await ref.read(productsRepositoryProvider.future);

    // Calculate pagination parameters
    final limitStart = _currentPage * pageSize;

    // Fetch next page from API
    final getProductsUseCase = GetProducts(repository);
    var newProducts = await getProductsUseCase(
      limitStart: limitStart,
      limitPageLength: pageSize,
    );

    // Filter by brand if not 'all'
    if (selectedBrand != 'all') {
      newProducts = newProducts
          .where((product) => product.brand == selectedBrand)
          .toList();
    }

    // If we got less than pageSize, there are no more products
    _hasMore = newProducts.length >= pageSize;

    // Increment page counter
    _currentPage++;

    // Append new products to existing list
    final currentProducts = state.value ?? [];
    state = AsyncValue.data([...currentProducts, ...newProducts]);
  }

  /// Check if there are more products to load
  bool get hasMore => _hasMore;

  /// Refresh products data
  ///
  /// Forces a refresh from the datasource.
  Future<void> refresh() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() => build());
  }
}

/// All Products Provider (no filters)
///
/// Provides all products without any filtering.
/// Useful for product selection dialogs, etc.
/// Fetches from Frappe ERPNext API.
@riverpod
Future<List<Product>> allProducts(Ref ref) async {
  final repository = await ref.watch(productsRepositoryProvider.future);
  final getProductsUseCase = GetProducts(repository);

  return await getProductsUseCase();
}

/// Product Detail Provider
///
/// Fetches a single product by ID from Frappe ERPNext API.
/// Uses getProductDetail endpoint for efficient single product fetch.
///
/// Usage:
/// ```dart
/// final productAsync = ref.watch(productDetailProvider(productId: 'GIB20 G02'));
///
/// productAsync.when(
///   data: (product) => ProductDetailView(product: product),
///   loading: () => CircularProgressIndicator(),
///   error: (error, stack) => ErrorWidget(error),
/// );
/// ```
@riverpod
Future<Product> productDetail(Ref ref, {required String productId}) async {
  final repository = await ref.watch(productsRepositoryProvider.future);
  final getProductDetailUseCase = GetProductDetail(repository);

  return await getProductDetailUseCase(productId: productId);
}
