/// Order Remote Data Source
///
/// Handles API calls for order-related data.
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/features/orders/data/models/order_status_model.dart';
import 'package:worker/features/orders/data/models/payment_term_model.dart';

/// Order Remote Data Source
class OrderRemoteDataSource {
  const OrderRemoteDataSource(this._dioClient);

  final DioClient _dioClient;

  /// Get order status list
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.get_order_status_list
  /// Returns: List of order statuses with labels and colors
  Future<List<OrderStatusModel>> getOrderStatusList() async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getOrderStatusList}',
        data: <String, dynamic>{},
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getOrderStatusList API');
      }

      // API returns: { "message": [...] }
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getOrderStatusList response');
      }

      final List<dynamic> statusList = message as List<dynamic>;
      return statusList
          .map((json) => OrderStatusModel.fromJson(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      throw Exception('Failed to get order status list: $e');
    }
  }

  /// Get payment terms list
  ///
  /// Calls: POST /api/method/frappe.client.get_list
  /// Body: { "doctype": "Payment Terms Template", "fields": ["name","custom_description"], "limit_page_length": 0 }
  /// Returns: List of payment terms with names and descriptions
  Future<List<PaymentTermModel>> getPaymentTermsList() async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}',
        data: {
          'doctype': 'Payment Terms Template',
          'fields': ['name', 'custom_description'],
          'limit_page_length': 0,
        },
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getPaymentTermsList API');
      }

      // API returns: { "message": [...] }
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getPaymentTermsList response');
      }

      final List<dynamic> termsList = message as List<dynamic>;
      return termsList
          .map((json) => PaymentTermModel.fromJson(json as Map<String, dynamic>))
          .toList();
    } catch (e) {
      throw Exception('Failed to get payment terms list: $e');
    }
  }

  /// Create new order
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.save
  /// Body: {
  ///   "transaction_date": "2025-11-20",
  ///   "delivery_date": "2025-11-20",
  ///   "shipping_address_name": "...",
  ///   "customer_address": "...",
  ///   "description": "...",
  ///   "payment_terms": "...",
  ///   "ignore_pricing_rule": true,
  ///   "contract_request": true,
  ///   "items": [{"item_id": "...", "qty_entered": 0, "primary_qty": 0, "price_entered": 0}]
  /// }
  /// Returns: { "message": { "name": "SAL-ORD-2025-00001", ... } }
  Future<Map<String, dynamic>> createOrder({
    required List<Map<String, dynamic>> items,
    required Map<String, dynamic> deliveryAddress,
    required String paymentMethod,
    bool needsInvoice = false,
    bool ignorePricingRule = false,
    bool contractRequest = false,
    String? notes,
  }) async {
    try {
      // Get current date for transaction and delivery
      final now = DateTime.now();
      final dateStr = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';

      // Format items for Frappe API
      final formattedItems = items.map((item) {
        return {
          'item_id': item['item_id'],
          'qty_entered': item['quantity'], // boxes/pieces (viên)
          'primary_qty': item['quantityConverted'], // m²
          'price_entered': item['price'],
        };
      }).toList();

      // Prepare request body in Frappe format
      final requestBody = {
        'transaction_date': dateStr,
        'delivery_date': dateStr,
        'shipping_address_name': deliveryAddress['name'] ?? '',
        'customer_address': deliveryAddress['name'] ?? '',
        'description': notes ?? 'Order from mobile app',
        'payment_terms': paymentMethod,
        'ignore_pricing_rule': ignorePricingRule,
        'contract_request': contractRequest,
        'items': formattedItems,
      };

      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.createOrder}',
        data: requestBody,
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from createOrder API');
      }

      // Extract order info from Frappe response
      // Response format: { message: { success: true, message: "...", data: { name: "SAL-ORD-2025-00078", ... } } }
      final message = data['message'] as Map<String, dynamic>?;
      if (message == null) {
        throw Exception('No message field in createOrder response');
      }

      final orderData = message['data'] as Map<String, dynamic>?;
      if (orderData == null) {
        throw Exception('No data field in createOrder response');
      }

      final orderId = orderData['name'] as String?;
      if (orderId == null || orderId.isEmpty) {
        throw Exception('No order ID (name) in createOrder response');
      }

      // Return standardized response
      return {
        'orderId': orderId,
        'orderNumber': orderId,
        'fullResponse': orderData,
      };
    } catch (e) {
      throw Exception('Failed to create order: $e');
    }
  }

  /// Generate QR code for payment
  ///
  /// Calls: POST /api/method/building_material.building_material.api.v1.qrcode.generate
  /// Body: { "order_id": "SAL-ORD-2025-00048" }
  /// Returns: { "qr_code": "...", "amount": null, "transaction_id": "...", "bank_info": {...} }
  Future<Map<String, dynamic>> generateQrCode(String orderId) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.generateQrCode}',
        data: {'order_id': orderId},
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from generateQrCode API');
      }

      // Extract QR code info from Frappe response
      final message = data['message'] as Map<String, dynamic>?;
      if (message == null) {
        throw Exception('No message field in generateQrCode response');
      }

      return message;
    } catch (e) {
      throw Exception('Failed to generate QR code: $e');
    }
  }

  /// Upload bill/invoice file
  ///
  /// Calls: POST /api/method/upload_file
  /// Form-data: {
  ///   "file": File,
  ///   "is_private": "1",
  ///   "folder": "Home/Attachments",
  ///   "doctype": "Sales Order",
  ///   "docname": "SAL-ORD-2025-00058-1",
  ///   "optimize": "true"
  /// }
  /// Returns: { "message": { "file_url": "...", "file_name": "...", ... } }
  Future<Map<String, dynamic>> uploadBill({
    required String filePath,
    required String orderId,
  }) async {
    try {
      // Create multipart form data
      final formData = FormData.fromMap({
        'file': await MultipartFile.fromFile(
          filePath,
          filename: filePath.split('/').last,
        ),
        'is_private': '1',
        'folder': 'Home/Attachments',
        'doctype': 'Sales Order',
        'docname': orderId,
        'optimize': 'true',
      });

      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.uploadFile}',
        data: formData,
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from uploadBill API');
      }

      // Extract file info from Frappe response
      final message = data['message'] as Map<String, dynamic>?;
      if (message == null) {
        throw Exception('No message field in uploadBill response');
      }

      return message;
    } catch (e) {
      throw Exception('Failed to upload bill: $e');
    }
  }

  /// Get list of orders
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.get_list
  /// Body: { "limit_start": 0, "limit_page_length": 0 }
  /// Returns: List of orders
  Future<List<Map<String, dynamic>>> getOrdersList({
    int limitStart = 0,
    int limitPageLength = 0,
  }) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getOrdersList}',
        data: {
          'limit_start': limitStart,
          'limit_page_length': limitPageLength,
        },
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getOrdersList API');
      }

      // Extract orders list from Frappe response
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getOrdersList response');
      }

      if (message is! List) {
        throw Exception('Expected list but got ${message.runtimeType}');
      }

      return message.cast<Map<String, dynamic>>();
    } catch (e) {
      throw Exception('Failed to get orders list: $e');
    }
  }

  /// Get order detail
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.get_detail
  /// Body: { "name": "SAL-ORD-2025-00058-1" }
  /// Returns: Order details
  Future<Map<String, dynamic>> getOrderDetail(String orderName) async {
    try {
      final response = await _dioClient.post<Map<String, dynamic>>(
        '${ApiConstants.frappeApiMethod}${ApiConstants.getOrderDetail}',
        data: {'name': orderName},
      );

      final data = response.data;
      if (data == null) {
        throw Exception('No data received from getOrderDetail API');
      }

      // Extract order detail from Frappe response
      final message = data['message'];
      if (message == null) {
        throw Exception('No message field in getOrderDetail response');
      }

      if (message is! Map<String, dynamic>) {
        throw Exception('Expected map but got ${message.runtimeType}');
      }

      return message;
    } catch (e) {
      throw Exception('Failed to get order detail: $e');
    }
  }

  /// Update order address
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.update
  /// Body: {
  ///   "name": "SAL-ORD-2025-00053",
  ///   "shipping_address_name": "Công ty Tiến Nguyễn 2-thanh toán",
  ///   "customer_address": "Nguyễn Lê Duy Ti-Billing"
  /// }
  /// Updates shipping and billing addresses for an existing order
  Future<void> updateOrderAddress({
    required String orderId,
    required String shippingAddressName,
    required String customerAddress,
  }) async {
    try {
      await _dioClient.post(
        '${ApiConstants.frappeApiMethod}${ApiConstants.updateOrder}',
        data: {
          'name': orderId,
          'shipping_address_name': shippingAddressName,
          'customer_address': customerAddress,
        },
      );
    } catch (e) {
      throw Exception('Failed to update order address: $e');
    }
  }

  /// Cancel order
  ///
  /// Calls: POST /api/method/building_material.building_material.api.sales_order.cancel
  /// Body: { "name": "SAL-ORD-2025-00054" }
  /// Cancels an existing order (only allowed for "Chờ phê duyệt" status)
  Future<void> cancelOrder(String orderId) async {
    try {
      await _dioClient.post(
        '${ApiConstants.frappeApiMethod}${ApiConstants.cancelOrder}',
        data: {'name': orderId},
      );
    } catch (e) {
      throw Exception('Failed to cancel order: $e');
    }
  }
}
