import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';

import '../../../../core/network/dio_client.dart';
import '../../../../core/constants/api_constants.dart';
import '../../../../core/errors/exceptions.dart';
import '../../../transfer/data/models/currency_option_model.dart';
import '../models/withdraw_method_model.dart';
import '../models/withdraw_request_model.dart';
import '../models/withdraw_response_model.dart';
import 'withdraw_remote_datasource.dart';

@Injectable(as: WithdrawRemoteDataSource)
class WithdrawRemoteDataSourceImpl implements WithdrawRemoteDataSource {
  final DioClient _dioClient;

  const WithdrawRemoteDataSourceImpl(this._dioClient);

  @override
  Future<List<CurrencyOptionModel>> getWithdrawCurrencies({
    required String walletType,
  }) async {
    try {
      print('📍 Fetching withdraw currencies for wallet type: $walletType');

      final response = await _dioClient.get(
        ApiConstants.withdrawCurrencies,
        queryParameters: {
          'action': 'withdraw',
          'walletType': walletType,
        },
      );

      print('✅ Response received');
      print('📦 Response data: ${response.data}');
      print('📦 Response type: ${response.data.runtimeType}');

      if (response.data == null) {
        print('⚠️ Response data is null');
        return [];
      }

      // Handle different response structures
      List<dynamic> currencyList = [];

      if (response.data is Map) {
        print('📦 Response is Map');
        final responseMap = response.data as Map<String, dynamic>;

        // Check for 'data' field first
        if (responseMap.containsKey('data') && responseMap['data'] is List) {
          currencyList = responseMap['data'] as List;
          print('📦 Found data field with ${currencyList.length} items');
        }
        // Check if the response itself contains currency data
        else if (responseMap.containsKey('currencies') &&
            responseMap['currencies'] is List) {
          currencyList = responseMap['currencies'] as List;
          print('📦 Found currencies field with ${currencyList.length} items');
        }
        // Handle single currency response
        else if (responseMap.containsKey('value') &&
            responseMap.containsKey('label')) {
          currencyList = [responseMap];
          print('📦 Single currency response');
        }
      } else if (response.data is List) {
        currencyList = response.data as List;
        print('📦 Response is List with ${currencyList.length} items');
      }

      if (currencyList.isEmpty) {
        print('⚠️ No currencies found in response');
        return [];
      }

      print('💰 Found ${currencyList.length} currencies for $walletType');
      return currencyList.map((json) {
        final currencyData = json as Map<String, dynamic>;

        // Extract balance from label if not provided separately
        double? balance;
        if (currencyData['balance'] != null) {
          balance = (currencyData['balance'] as num).toDouble();
        } else if (currencyData['label'] != null) {
          // Label format: "USD - 5323.6" or "BTC - 33.8"
          final labelParts = currencyData['label'].toString().split(' - ');
          if (labelParts.length > 1) {
            balance = double.tryParse(labelParts[1]);
            print(
                '💵 Extracted balance from label: ${currencyData['value']} = $balance');
          }
        }

        // Create model with extracted balance
        return CurrencyOptionModel(
          value: currencyData['value'] ?? '',
          label: currencyData['label'] ?? '',
          icon: currencyData['icon'],
          balance: balance,
        );
      }).toList();
    } on DioException catch (e) {
      print('❌ DioException fetching currencies for $walletType:');
      print('   Status Code: ${e.response?.statusCode}');
      print('   Message: ${e.response?.data}');
      print('   Error Type: ${e.type}');

      if (e.response?.statusCode == 404) {
        print('🔍 No wallets with balance found for $walletType');
        return [];
      }

      throw ServerException(e.response?.data?['message'] ??
          e.response?.data?.toString() ??
          'Failed to fetch currencies for $walletType');
    } catch (e) {
      print('❌ Unexpected error fetching currencies for $walletType: $e');
      throw ServerException(e.toString());
    }
  }

  @override
  Future<List<WithdrawMethodModel>> getWithdrawMethods({
    required String walletType,
    required String currency,
  }) async {
    try {
      final response = await _dioClient.get(
        '${ApiConstants.withdrawMethods}/$walletType/$currency',
        queryParameters: {
          'action': 'withdraw',
        },
      );

      List<WithdrawMethodModel> methods = [];

      if (walletType == 'FIAT') {
        // For FIAT, response contains { methods: [...] }
        final List<dynamic> methodsList = response.data['methods'] ?? [];
        methods = methodsList
            .map((json) =>
                WithdrawMethodModel.fromJson(json as Map<String, dynamic>))
            .toList();
      } else {
        // For SPOT/ECO, response is an array of network methods
        final List<dynamic> networksList = response.data is List
            ? response.data
            : response.data['networks'] ?? [];

        // Convert network data to WithdrawMethodModel format
        methods = networksList.map((network) {
          final networkData = network as Map<String, dynamic>;

          // Extract fee - can be in different formats
          double fixedFee = 0.0;
          if (networkData['fee'] != null) {
            fixedFee = (networkData['fee'] as num).toDouble();
          } else if (networkData['fees'] != null &&
              networkData['fees']['withdraw'] != null) {
            fixedFee = (networkData['fees']['withdraw'] as num).toDouble();
          }

          return WithdrawMethodModel(
            id: networkData['id'] ?? networkData['network'] ?? '',
            title: networkData['name'] ?? networkData['network'] ?? '',
            network: networkData['network'],
            fixedFee: fixedFee,
            minAmount: networkData['min_withdraw']?.toDouble(),
            maxAmount: networkData['max_withdraw']?.toDouble(),
            customFields: _generateCustomFieldsForNetwork(networkData),
            isActive: networkData['active'] ?? true,
            percentageFee:
                0.0, // Network fees are fixed, percentage comes from currency
          );
        }).toList();
      }

      return methods;
    } on DioException catch (e) {
      throw ServerException(
        e.response?.data?['message'] ?? 'Failed to fetch withdrawal methods',
      );
    }
  }

  @override
  Future<WithdrawResponseModel> submitWithdrawal(
    WithdrawRequestModel request,
  ) async {
    try {
      String endpoint;
      Map<String, dynamic> requestBody = {};

      if (request.walletType == 'FIAT') {
        endpoint = ApiConstants.withdrawFiat;
        requestBody = {
          'methodId': request.methodId,
          'amount': request.amount,
          'currency': request.currency,
          'customFields': request.customFields ?? {},
        };
      } else if (request.walletType == 'SPOT' ||
          request.walletType == 'ECO' ||
          request.walletType == 'FUTURES') {
        endpoint = ApiConstants.withdrawSpot;

        // Extract address from custom fields if needed
        String? toAddress = request.toAddress;
        if (toAddress == null && request.customFields != null) {
          final addressField = request.customFields!.keys.firstWhere(
            (key) => key.toLowerCase().contains('address'),
            orElse: () => '',
          );
          if (addressField.isNotEmpty) {
            toAddress = request.customFields![addressField];
          }
        }

        requestBody = {
          'currency': request.currency,
          'chain': request.chain ?? request.methodId,
          'amount': request.amount,
          'toAddress': toAddress,
          if (request.memo != null) 'memo': request.memo,
          ...?request.customFields,
        };
      } else {
        throw ServerException('Unsupported wallet type: ${request.walletType}');
      }

      final response = await _dioClient.post(
        endpoint,
        data: requestBody,
      );

      return WithdrawResponseModel.fromJson(response.data);
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        throw ServerException(
          e.response?.data?['message'] ?? 'Invalid withdrawal request',
        );
      } else if (e.response?.statusCode == 401) {
        throw AuthException('Authentication required');
      } else if (e.response?.statusCode == 403) {
        throw AuthException('Insufficient permissions');
      } else {
        throw ServerException(
          e.response?.data?['message'] ?? 'Failed to process withdrawal',
        );
      }
    }
  }

  // Helper method to generate custom fields JSON string for network-based withdrawals
  String _generateCustomFieldsForNetwork(Map<String, dynamic> networkData) {
    final fields = [
      {
        'name': 'address',
        'title': 'Withdrawal Address',
        'type': 'text',
        'required': true,
        'placeholder': 'Enter your ${networkData['network'] ?? ''} address',
      },
    ];

    // Add memo field if supported - check various possible field names
    final supportsMemo = networkData['supportsMemo'] == true ||
        networkData['hasMemo'] == true ||
        networkData['has_memo'] == true ||
        networkData['requires_memo'] == true ||
        networkData['requiresMemo'] == true;

    if (supportsMemo) {
      fields.add({
        'name': 'memo',
        'title': 'Memo/Tag',
        'type': 'text',
        'required': networkData['requires_memo'] == true ||
            networkData['requiresMemo'] == true,
        'placeholder': 'Enter memo or tag if required by the exchange',
      });
    }

    return json.encode(fields);
  }
}
