/// 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/get_product_detail.dart';
import 'package:worker/features/products/presentation/providers/product_filters_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: () => const CustomLoadingIndicator(),
///   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 {
    // IMPORTANT: This method is called automatically whenever any watched
    // provider changes (searchQueryProvider, productFiltersProvider, etc.)
    // This ensures pagination is ALWAYS reset when filters/search change.

    // Reset pagination state
    _currentPage = 0;
    _hasMore = true;

    // Watch dependencies (triggers rebuild when they change)
    final searchQuery = ref.watch(searchQueryProvider);
    // Watch APPLIED filters, not pending filters (API only called on Apply)
    final filters = ref.watch(appliedProductFiltersProvider);

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

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

    // Check if any filters or search are active
    final hasFilters = filters.hasActiveFilters;
    final hasSearch = searchQuery.isNotEmpty;

    if (!hasFilters && !hasSearch) {
      // No filters/search: Use simple getAllProducts for faster initial load
      products = await repository.getAllProducts(
        limitStart: 0,
        limitPageLength: pageSize,
      );
    } else {
      // Filters/search active: Use getProductsWithFilters
      final List<String>? itemGroups = filters.productLines.isNotEmpty
          ? filters.productLines.toList()
          : null;

      final List<String>? brands = filters.brands.isNotEmpty
          ? filters.brands.toList()
          : null;

      // Build item attributes from filter drawer (sizes, surfaces, colors)
      final List<Map<String, String>> itemAttributes = [];

      for (final size in filters.sizes) {
        itemAttributes.add({'attribute': 'Kích thước', 'attribute_value': size});
      }

      for (final surface in filters.surfaces) {
        itemAttributes.add({'attribute': 'Bề mặt', 'attribute_value': surface});
      }

      for (final color in filters.colors) {
        itemAttributes.add({'attribute': 'Màu sắc', 'attribute_value': color});
      }

      final String? keyword = hasSearch ? searchQuery : null;

      products = await repository.getProductsWithFilters(
        limitStart: 0,
        limitPageLength: pageSize,
        itemGroups: itemGroups,
        brands: brands,
        itemAttributes: itemAttributes.isNotEmpty ? itemAttributes : null,
        searchKeyword: keyword,
      );
    }

    // 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;

    // Read dependencies to get current filters (use read, not watch)
    final searchQuery = ref.read(searchQueryProvider);
    // Read APPLIED filters, not pending filters
    final filters = ref.read(appliedProductFiltersProvider);

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

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

    // Check if any filters or search are active
    final hasFilters = filters.hasActiveFilters;
    final hasSearch = searchQuery.isNotEmpty;

    List<Product> newProducts;

    if (!hasFilters && !hasSearch) {
      // No filters/search: Use simple getAllProducts
      newProducts = await repository.getAllProducts(
        limitStart: limitStart,
        limitPageLength: pageSize,
      );
    } else {
      // Filters/search active: Use getProductsWithFilters
      final List<String>? itemGroups = filters.productLines.isNotEmpty
          ? filters.productLines.toList()
          : null;

      final List<String>? brands = filters.brands.isNotEmpty
          ? filters.brands.toList()
          : null;

      final List<Map<String, String>> itemAttributes = [];

      for (final size in filters.sizes) {
        itemAttributes.add({'attribute': 'Kích thước', 'attribute_value': size});
      }

      for (final surface in filters.surfaces) {
        itemAttributes.add({'attribute': 'Bề mặt', 'attribute_value': surface});
      }

      for (final color in filters.colors) {
        itemAttributes.add({'attribute': 'Màu sắc', 'attribute_value': color});
      }

      final String? keyword = hasSearch ? searchQuery : null;

      newProducts = await repository.getProductsWithFilters(
        limitStart: limitStart,
        limitPageLength: pageSize,
        itemGroups: itemGroups,
        brands: brands,
        itemAttributes: itemAttributes.isNotEmpty ? itemAttributes : null,
        searchKeyword: keyword,
      );
    }

    // 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: () => const CustomLoadingIndicator(),
///   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);
}
