import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class PaymobResponseWallet {
  final bool success;
  final String? transactionID;
  final String? responseCode;
  final String? message;

  PaymobResponseWallet({
    required this.success,
    this.transactionID,
    this.responseCode,
    this.message,
  });

  factory PaymobResponseWallet.fromJson(Map<String, dynamic> json) {
    return PaymobResponseWallet(
      success: json['success'] == 'true',
      transactionID: json['id'],
      responseCode: json['txn_response_code'],
      message: json['data']['message'],
    );
  }
}

class PaymobPaymentWallet {
  static PaymobPaymentWallet instance = PaymobPaymentWallet();

  bool _isInitializedWallet = false;

  final Dio _dio = Dio();
  final _baseURL = 'https://accept.paymob.com/api/';
  late String _apiKey;
  late int _integrationID;
  late int _iFrameID;
  late String _iFrameURL;
  late int _userTokenExpiration;

  /// Initializing PaymobPayment instance.
  Future<bool> initialize({
    /// It is a unique identifier for the merchant which used to authenticate your requests calling any of Accept's API.
    /// from dashboard Select Settings -> Account Info -> API Key
    required String apiKey,

    /// from dashboard Select Developers -> Payment Integrations -> Online Card ID
    required int integrationID,

    /// from paymob Select Developers -> iframes
    required int iFrameID,

    /// The expiration time of this payment token in seconds. (The maximum is 3600 seconds which is an hour)
    int userTokenExpiration = 300,
  }) async {
    if (_isInitializedWallet) {
      return true;
    }
    _dio.options.baseUrl = _baseURL;
    _dio.options.validateStatus = (status) => true;
    _apiKey = apiKey;
    _integrationID = integrationID;
    _iFrameID = iFrameID;
    _iFrameURL =
        'https://accept.paymobsolutions.com/api/acceptance/iframes/$_iFrameID?payment_token=';
    _isInitializedWallet = true;
    _userTokenExpiration = userTokenExpiration;
    return _isInitializedWallet;
  }

  /// Get authentication token, which is valid for one hour from the creation time.
  Future<String> _getAuthToken() async {
    try {
      final response = await _dio.post(
        'auth/tokens',
        data: {
          'api_key': _apiKey,
        },
      );
      print(response.data['token']);
      return response.data['token'];
    } catch (e) {
      rethrow;
    }
  }

  /// At this step, you will register an order to Accept's database, so that you can pay for it later using a transaction
  Future<int> _addOrder({
    required String authToken,
    required String currency,
    required String amount,
    required List items,
  }) async {
    try {
      final response = await _dio.post(
        'ecommerce/orders',
        data: {
          "auth_token": authToken,
          "delivery_needed": "false",
          "amount_cents": amount,
          "currency": currency,
          "items": items,
        },
      );
      print(response.data['id']);
      return response.data['id'];
    } catch (e) {
      rethrow;
    }
  }

  /// At this step, you will obtain a payment_key token. This key will be used to authenticate your payment request. It will be also used for verifying your transaction request metadata.
  Future<String> _getPurchaseToken({
    required String authToken,
    required String currency,
    required int orderID,
    required String amount,
    required PaymobBillingDataWallet billingData,
  }) async {
    final response = await _dio.post(
      'acceptance/payment_keys',
      data: {
        "auth_token": authToken,
        "amount_cents": amount,
        "expiration": _userTokenExpiration,
        "order_id": orderID,
        "billing_data": billingData,
        "currency": currency,
        "integration_id": _integrationID,
        "lock_order_when_paid": "false"
      },
    );
    // final message = response.data['message'];
    // if (message != null) {
    //   throw Exception(message);
    // }
    print(response.data['token']);
    return response.data['token'];
  }

  Future<String> _getWalletUrl({
    required String paymentToken,
  }) async {
    final Map<String, dynamic> data = {
      "source": {
        "identifier": "01010101010", // Replace with actual source identifier
        "subtype": "WALLET",
      },
      "payment_token": paymentToken,
    };
    final dio = Dio();
    try {
      final response = await dio.post(
        'https://accept.paymobsolutions.com/api/acceptance/payments/pay',
        data: data,
      );

      // 4. Handle Payment Response
      if (response.statusCode == 200) {
        final paymentData = response.data; // Assuming JSON response

        print("redirection_url: ${paymentData['iframe_redirection_url']}");
        return paymentData['iframe_redirection_url'];
        // Navigate to success screen or display success message
      } else {
        // Payment failed: Handle errors (e.g., display error message)
        print("Payment failed: ${response.statusCode} - ${response.data}");
      }
    } on DioError catch (e) {
      // Handle network or Dio-related errors
      print("Error making payment request: $e");
    }
    return '';
  }

  /// Proceed to pay with only calling this function.
  /// Opens a WebView at Paymob redirectedURL to accept user payment info.
  Future<PaymobResponseWallet?> pay(
      {
      /// BuildContext for navigation to WebView
      required BuildContext context,

      /// Which Currency you would pay in.
      required String currency,

      /// Payment amount in cents EX: 20000 is an 200 EGP
      required String amountInCents,

      /// Optional Callback if you can use return result of pay function or use this callback
      void Function(PaymobResponseWallet response)? onPayment,

      /// list of json objects contains the contents of the purchase.
      List? items,

      /// The billing data related to the customer related to this payment.
      PaymobBillingDataWallet? billingData}) async {
    if (!_isInitializedWallet) {
      throw Exception(
          'PaymobPayment is not initialized call:`PaymobPayment.instance.initialize`');
    }
    final authToken = await _getAuthToken();
    final orderID = await _addOrder(
      authToken: authToken,
      currency: currency,
      amount: amountInCents,
      items: items ?? [],
    );
    final purchaseToken = await _getPurchaseToken(
      authToken: authToken,
      currency: currency,
      orderID: orderID,
      amount: amountInCents,
      billingData: billingData ??
          PaymobBillingDataWallet(
              //   email: box.read(BoxName.email) ?? box.read(BoxName.emailDriver),
              //   firstName: box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
              //   lastName:
              //       box.read(BoxName.lastNameDriver) ?? box.read(BoxName.name),
              //   phoneNumber:
              //       box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver),
              ),
    );
    final urlWallet = await _getWalletUrl(paymentToken: purchaseToken);

    if (context.mounted) {
      final response = await PaymobIFrameWallet.show(
        context: context,
        redirectURL: urlWallet,
        onPayment: onPayment,
      );
      return response;
    }
    return null;
  }
}

class PaymobBillingDataWallet {
  String? email;
  String? firstName;
  String? lastName;
  String? phoneNumber;
  String? apartment;
  String? floor;
  String? street;
  String? building;
  String? postalCode;
  String? city;
  String? state;
  String? country;
  String? shippingMethod;

  PaymobBillingDataWallet({
    this.email,
    this.firstName,
    this.lastName,
    this.phoneNumber,
    this.apartment,
    this.floor,
    this.street,
    this.building,
    this.postalCode,
    this.city,
    this.state,
    this.country,
    this.shippingMethod,
  });

  Map<String, dynamic> toJson() {
    return {
      "email": box.read(BoxName.email) ?? box.read(BoxName.emailDriver),
      "first_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
      "last_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
      "phone_number": box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver),
      "apartment": apartment ?? "NA",
      "floor": floor ?? "NA",
      "building": building ?? "NA",
      "street": street ?? "NA",
      "postal_code": postalCode ?? "NA",
      "city": city ?? "NA",
      "state": state ?? "NA",
      "country": country ?? "NA",
      "shipping_method": shippingMethod ?? "NA",
    };
  }
}

class PaymobIFrameWallet extends StatefulWidget {
  const PaymobIFrameWallet({
    Key? key,
    required this.redirectURL,
    this.onPayment,
  }) : super(key: key);

  final String redirectURL;
  final void Function(PaymobResponseWallet)? onPayment;

  static Future<PaymobResponseWallet?> show({
    required BuildContext context,
    required String redirectURL,
    void Function(PaymobResponseWallet)? onPayment,
  }) =>
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) {
            return PaymobIFrameWallet(
              onPayment: onPayment,
              redirectURL: redirectURL,
            );
          },
        ),
      );

  @override
  State<PaymobIFrameWallet> createState() => _PaymobIFrameState();
}

class _PaymobIFrameState extends State<PaymobIFrameWallet> {
  WebViewController? controller;

  @override
  void initState() {
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onNavigationRequest: (NavigationRequest request) {
            if (request.url.contains('txn_response_code') &&
                // request.url.contains('successfully') &&
                request.url.contains('success') &&
                request.url.contains('id')) {
              final params = _getParamFromURL(request.url);
              final response = PaymobResponseWallet.fromJson(params);
              if (widget.onPayment != null) {
                widget.onPayment!(response);
              }
              Navigator.pop(context, response);
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(Uri.parse(widget.redirectURL));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: controller == null
          ? const Center(
              child: CircularProgressIndicator.adaptive(),
            )
          : SafeArea(
              child: WebViewWidget(
                controller: controller!,
              ),
            ),
    );
  }

  Map<String, dynamic> _getParamFromURL(String url) {
    final uri = Uri.parse(url);
    final queryParams = uri.queryParameters;
    final data = <String, dynamic>{};

    queryParams.forEach((key, value) {
      if (key.contains('.')) {
        final parts = key.split('.');
        data.putIfAbsent(parts.first, () => <String, dynamic>{});
        (data[parts.first] as Map<String, dynamic>)[parts.last] = value;
      } else {
        data[key] = value;
      }
    });

    return data;
  }
}
