import 'dart:developer';

import 'package:dartz/dartz.dart';
import 'package:data_channel/data_channel.dart';
import 'package:injectable/injectable.dart';
import 'package:intl/intl.dart';

import '../../../common/api/api_client.dart';
import '../../../common/api/api_failure.dart';
import '../../../common/function/app_function.dart';
import '../../../common/url/api_path.dart';
import '../../../domain/order/order.dart';
import '../order_dtos.dart';

@injectable
class OrderRemoteDataProvider {
  final ApiClient _apiClient;
  final _logName = 'OrderRemoteDataProvider';
  OrderRemoteDataProvider(this._apiClient);

  /// Helper untuk merge auth header dengan idempotency key
  Map<String, dynamic> _buildHeaders({String? idempotencyKey}) {
    final headers = getAuthorizationHeader();
    if (idempotencyKey != null) {
      headers['X-Idempotency-Key'] = idempotencyKey;
    }
    return headers;
  }

  Future<DC<OrderFailure, ListOrderDto>> fetchOrders({
    int page = 1,
    int limit = 10,
    String status = 'completed',
    required DateTime startDate,
    required DateTime endDate,
    String? search,
  }) async {
    try {
      Map<String, dynamic> params = {
        'page': page,
        'limit': limit,
        'status': status,
        'date_from': DateFormat('dd-MM-yyyy').format(startDate),
        'date_to': DateFormat('dd-MM-yyyy').format(endDate),
      };

      if (search != null && search.isNotEmpty) {
        params['search'] = search;
      }

      final response = await _apiClient.get(
        ApiPath.orders,
        params: params,
        headers: getAuthorizationHeader(),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final orders = ListOrderDto.fromJson(
        response.data['data'] as Map<String, dynamic>,
      );

      return DC.data(orders);
    } on ApiFailure catch (e, s) {
      log('fetchOrderError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, OrderDto>> fetchOrderById(String id) async {
    try {
      final response = await _apiClient.get(
        '${ApiPath.orders}/$id',
        headers: getAuthorizationHeader(),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final order = OrderDto.fromJson(
        response.data['data'] as Map<String, dynamic>,
      );

      return DC.data(order);
    } on ApiFailure catch (e, s) {
      log('fetchOrderByIdError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, OrderDto>> storeOrder({
    required OrderRequestDto request,
  }) async {
    try {
      final response = await _apiClient.post(
        ApiPath.orders,
        data: request.toRequest(),
        headers: getAuthorizationHeader(),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final order = OrderDto.fromJson(
        response.data['data'] as Map<String, dynamic>,
      );

      return DC.data(order);
    } on ApiFailure catch (e, s) {
      log('storeOrderError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, OrderDto>> storeWithPaymentOrder({
    required OrderRequestDto request,
    required String paymentMethodId,
  }) async {
    try {
      final response = await _apiClient.post(
        ApiPath.orders,
        data: request.toRequest(),
        headers: getAuthorizationHeader(),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      } else {
        final order = OrderDto.fromJson(
          response.data['data'] as Map<String, dynamic>,
        );
        final paymentRequest = PaymentRequestDto(
          orderId: order.id,
          amount: order.totalAmount,
          paymentMethodId: paymentMethodId,
          splitNumber: 1,
          splitTotal: 1,
          splitDescription: '',
          paymentOrderItems: order.orderItems
              ?.map(
                (item) => PaymentItemRequestDto(
                  amount: item.totalPrice,
                  orderItemId: item.id,
                ),
              )
              .toList(),
        );
        storePayment(paymentRequest);

        return DC.data(order);
      }
    } on ApiFailure catch (e, s) {
      log('storeOrderError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, PaymentDto>> storePayment(
    PaymentRequestDto request, {
    String? idempotencyKey,
  }) async {
    try {
      final response = await _apiClient.post(
        ApiPath.payments,
        data: request.toJson(),
        headers: _buildHeaders(idempotencyKey: idempotencyKey),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final payment = PaymentDto.fromJson(
        response.data['data'] as Map<String, dynamic>,
      );

      return DC.data(payment);
    } on ApiFailure catch (e, s) {
      log('storePaymentError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, OrderDto>> addItemOrder({
    required String id,
    required List<AddItemOrderRequestDto> request,
    String? idempotencyKey,
  }) async {
    try {
      final response = await _apiClient.post(
        '${ApiPath.orders}/$id/add-items',
        data: {
          'notes': '',
          'order_items': request.map((e) => e.toRequest()).toList(),
        },
        headers: _buildHeaders(idempotencyKey: idempotencyKey),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final order = OrderDto.fromJson(
        response.data['data']['updated_order'] as Map<String, dynamic>,
      );

      return DC.data(order);
    } on ApiFailure catch (e, s) {
      log('addItemOrderError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, Unit>> voidOrder({
    required String orderId,
    required String reason,
    String type = "ITEM", // TYPE: ALL, ITEM
    required List<OrderItemDto> orderItems,
    String? idempotencyKey,
  }) async {
    try {
      final response = await _apiClient.post(
        '${ApiPath.orders}/void',
        data: {
          'order_id': orderId,
          'type': orderItems.isEmpty ? "ALL" : type,
          'reason': reason,
          "items": orderItems
              .map(
                (item) => {'order_item_id': item.id, "quantity": item.quantity},
              )
              .toList(),
        },
        headers: _buildHeaders(idempotencyKey: idempotencyKey),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      return DC.data(unit);
    } on ApiFailure catch (e, s) {
      log('voidOrderError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, PaymentDto>> createSplitBill(
    PaymentSplitBillRequestDto request, {
    String? idempotencyKey,
  }) async {
    log(request.toRequest().toString());
    try {
      final response = await _apiClient.post(
        "${ApiPath.orders}/split-bill",
        data: request.toRequest(),
        headers: _buildHeaders(idempotencyKey: idempotencyKey),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      final payment = PaymentDto.fromJson(
        response.data['data'] as Map<String, dynamic>,
      );

      return DC.data(payment);
    } on ApiFailure catch (e, s) {
      log('createSplitBillError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }

  Future<DC<OrderFailure, Unit>> refundPayment({
    required String id,
    required String reason,
    required int refundAmount,
    String? idempotencyKey,
  }) async {
    try {
      final response = await _apiClient.post(
        '${ApiPath.orders}/$id/refund',
        data: {'refund_amount': refundAmount, 'reason': reason},
        headers: _buildHeaders(idempotencyKey: idempotencyKey),
      );

      if (response.data['success'] == false) {
        return DC.error(const OrderFailure.unexpectedError());
      }

      return DC.data(unit);
    } on ApiFailure catch (e, s) {
      log('refundPaymnetError', name: _logName, error: e, stackTrace: s);
      return DC.error(OrderFailure.serverError(e));
    }
  }
}
