/// Data Source: Products Remote Data Source
///
/// Handles fetching product data from the Frappe ERPNext API.
library;

import 'package:dio/dio.dart';
import 'package:worker/core/constants/api_constants.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/core/services/frappe_auth_service.dart';
import 'package:worker/features/products/data/models/product_model.dart';

/// Products Remote Data Source
///
/// Provides methods to fetch product data from the Frappe ERPNext API.
/// Uses FrappeAuthService for session management.
class ProductsRemoteDataSource {
  ProductsRemoteDataSource(this._dioClient, this._frappeAuthService);

  final DioClient _dioClient;
  final FrappeAuthService _frappeAuthService;

  /// Get all products
  ///
  /// Fetches all products from Frappe ERPNext.
  /// Returns a list of [ProductModel].
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/building_material.building_material.api.item.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "limit_start": 0,
  ///   "limit_page_length": 0
  /// }
  /// ```
  ///
  /// Response format:
  /// ```json
  /// {
  ///   "message": [
  ///     {
  ///       "name": "GIB20 G02",
  ///       "item_name": "Gạch Eurotile 1200x1200",
  ///       "description": "Product description...",
  ///       "standard_rate": 450000.0,
  ///       "stock_uom": "m²",
  ///       "image": "/files/image.jpg",
  ///       "brand": "Eurotile",
  ///       ...
  ///     }
  ///   ]
  /// }
  /// ```
  Future<List<ProductModel>> getAllProducts({
    int limitStart = 0,
    int limitPageLength = 0,
  }) async {
    try {
      // Get Frappe session headers
      final headers = await _frappeAuthService.getHeaders();

      // Build full API URL
      const url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetItems}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {'limit_start': limitStart, 'limit_page_length': limitPageLength},
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      // Parse the response
      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      final productsList = message as List;
      return productsList
          .map(
            (item) => ProductModel.fromJson(item as Map<String, dynamic>),
          )
          .toList();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Products endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching products');
      } else if (e.type == DioExceptionType.connectionTimeout) {
        throw Exception('Connection timeout while fetching products');
      } else if (e.type == DioExceptionType.receiveTimeout) {
        throw Exception('Response timeout while fetching products');
      } else {
        throw Exception('Failed to fetch products: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching products: $e');
    }
  }

  /// Get product detail by name/code
  ///
  /// Fetches a single product by its item code from Frappe ERPNext.
  /// Returns a [ProductModel].
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/building_material.building_material.api.item.get_detail
  /// Request body:
  /// ```json
  /// {
  ///   "name": "GIB20 G02"
  /// }
  /// ```
  Future<ProductModel> getProductDetail(String itemCode) async {
    try {
      // Get Frappe session headers
      final headers = await _frappeAuthService.getHeaders();

      // Build full API URL
      final url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetItemDetail}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {'name': itemCode},
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      // Parse the response
      final message = response.data!['message'];
      if (message == null) {
        throw Exception('Product not found: $itemCode');
      }

      return ProductModel.fromJson(message as Map<String, dynamic>);
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Product not found: $itemCode');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching product detail');
      } else if (e.type == DioExceptionType.connectionTimeout) {
        throw Exception('Connection timeout while fetching product detail');
      } else if (e.type == DioExceptionType.receiveTimeout) {
        throw Exception('Response timeout while fetching product detail');
      } else {
        throw Exception('Failed to fetch product detail: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching product detail: $e');
    }
  }

  /// Search products
  ///
  /// Searches products by keyword using Frappe API with pagination support.
  /// Uses the search_keyword parameter from the API.
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/building_material.building_material.api.item.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "limit_start": 0,
  ///   "limit_page_length": 12,
  ///   "search_keyword": "gạch men"
  /// }
  /// ```
  Future<List<ProductModel>> searchProducts(
    String query, {
    int limitStart = 0,
    int limitPageLength = 12,
  }) async {
    try {
      // Get Frappe session headers
      final headers = await _frappeAuthService.getHeaders();

      // Build full API URL
      const url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetItems}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {
          'limit_start': limitStart,
          'limit_page_length': limitPageLength,
          'search_keyword': query,
        },
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      // Parse the response
      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      final productsList = message as List;
      return productsList
          .map(
            (item) => ProductModel.fromJson(item as Map<String, dynamic>),
          )
          .toList();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Search endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while searching products');
      } else if (e.type == DioExceptionType.connectionTimeout) {
        throw Exception('Connection timeout while searching products');
      } else if (e.type == DioExceptionType.receiveTimeout) {
        throw Exception('Response timeout while searching products');
      } else {
        throw Exception('Failed to search products: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error searching products: $e');
    }
  }

  /// Get products by category
  ///
  /// Filters products by category with pagination support.
  /// For now, we fetch products with pagination and filter locally.
  /// In the future, the API might support category filtering.
  Future<List<ProductModel>> getProductsByCategory(
    String categoryId, {
    int limitStart = 0,
    int limitPageLength = 12,
  }) async {
    // Fetch products with pagination and filter locally
    // TODO: Implement server-side category filtering if API supports it
    final allProducts = await getAllProducts(
      limitStart: limitStart,
      limitPageLength: limitPageLength,
    );

    if (categoryId == 'all') {
      return allProducts;
    }

    return allProducts
        .where((product) => product.itemGroupName == categoryId)
        .toList();
  }

  /// Get product groups (Item Groups)
  ///
  /// Fetches product groups from Frappe ERPNext.
  /// Returns a list of group objects with name and item_group_name.
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/frappe.client.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "doctype": "Item Group",
  ///   "fields": ["item_group_name", "name"],
  ///   "filters": {"is_group": 0},
  ///   "limit_page_length": 0
  /// }
  /// ```
  Future<List<Map<String, dynamic>>> getProductGroups() async {
    try {
      final headers = await _frappeAuthService.getHeaders();
      final url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {
          'doctype': 'Item Group',
          'fields': ['item_group_name', 'name'],
          'filters': {'is_group': 0, 'custom_published': 1},
          'limit_page_length': 0,
        },
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      return (message as List).cast<Map<String, dynamic>>();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Product groups endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching product groups');
      } else {
        throw Exception('Failed to fetch product groups: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching product groups: $e');
    }
  }

  /// Get product brands
  ///
  /// Fetches brands from Frappe ERPNext.
  /// Returns a list of brand names.
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/frappe.client.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "doctype": "Brand",
  ///   "fields": ["name"],
  ///   "limit_page_length": 0
  /// }
  /// ```
  Future<List<String>> getProductBrands() async {
    try {
      final headers = await _frappeAuthService.getHeaders();
      final url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {
          'doctype': 'Brand',
          'fields': ['name'],
          'limit_page_length': 0,
        },
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      return (message as List).map((item) => item['name'] as String).toList();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Product brands endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching product brands');
      } else {
        throw Exception('Failed to fetch product brands: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching product brands: $e');
    }
  }

  /// Get product attributes
  ///
  /// Fetches product attributes from Frappe ERPNext.
  /// Returns a list of attribute objects.
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/building_material.building_material.api.item_attribute.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "filters": {"is_group": 0},
  ///   "limit_page_length": 0
  /// }
  /// ```
  Future<List<Map<String, dynamic>>> getProductAttributes() async {
    try {
      final headers = await _frappeAuthService.getHeaders();
      final url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetItemAttributes}';

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: {
          'filters': {'is_group': 0},
          'limit_page_length': 0,
        },
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      return (message as List).cast<Map<String, dynamic>>();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Product attributes endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching product attributes');
      } else {
        throw Exception('Failed to fetch product attributes: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching product attributes: $e');
    }
  }

  /// Get products with filters (Complete API - Final Version)
  ///
  /// Fetches products with support for all filter combinations:
  /// - item_group: List of product groups/categories
  /// - brand: List of brands
  /// - item_attribute: List of attribute filters (color, size, surface, etc.)
  /// - search_keyword: Search query
  /// - Pagination support
  ///
  /// API endpoint: POST https://land.dbiz.com/api/method/building_material.building_material.api.item.get_list
  /// Request body:
  /// ```json
  /// {
  ///   "limit_start": 0,
  ///   "limit_page_length": 12,
  ///   "item_group": ["CẨM THẠCH [ Marble ]"],
  ///   "brand": ["TEST 1"],
  ///   "item_attribute": [
  ///     {
  ///       "attribute": "Màu sắc",
  ///       "attribute_value": "Nhạt"
  ///     }
  ///   ],
  ///   "search_keyword": "gạch"
  /// }
  /// ```
  Future<List<ProductModel>> getProductsWithFilters({
    int limitStart = 0,
    int limitPageLength = 12,
    List<String>? itemGroups,
    List<String>? brands,
    List<Map<String, String>>? itemAttributes,
    String? searchKeyword,
  }) async {
    try {
      // Get Frappe session headers
      final headers = await _frappeAuthService.getHeaders();

      // Build full API URL
      const url =
          '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetItems}';

      // Build request data
      final Map<String, dynamic> requestData = {
        'limit_start': limitStart,
        'limit_page_length': limitPageLength,
      };

      // Add filters only if they have values
      if (itemGroups != null && itemGroups.isNotEmpty) {
        requestData['item_group'] = itemGroups;
      }

      if (brands != null && brands.isNotEmpty) {
        requestData['brand'] = brands;
      }

      if (itemAttributes != null && itemAttributes.isNotEmpty) {
        requestData['item_attribute'] = itemAttributes;
      }

      if (searchKeyword != null && searchKeyword.isNotEmpty) {
        requestData['search_keyword'] = searchKeyword;
      }

      final response = await _dioClient.post<Map<String, dynamic>>(
        url,
        data: requestData,
        options: Options(headers: headers),
      );

      if (response.data == null) {
        throw Exception('Empty response from server');
      }

      // Parse the response
      final message = response.data!['message'];
      if (message == null) {
        throw Exception('No message field in response');
      }

      final productsList = message as List;
      return productsList
          .map(
            (item) => ProductModel.fromJson(item as Map<String, dynamic>),
          )
          .toList();
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Products endpoint not found');
      } else if (e.response?.statusCode == 500) {
        throw Exception('Server error while fetching filtered products');
      } else if (e.type == DioExceptionType.connectionTimeout) {
        throw Exception('Connection timeout while fetching filtered products');
      } else if (e.type == DioExceptionType.receiveTimeout) {
        throw Exception('Response timeout while fetching filtered products');
      } else {
        throw Exception('Failed to fetch filtered products: ${e.message}');
      }
    } catch (e) {
      throw Exception('Unexpected error fetching filtered products: $e');
    }
  }
}
