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 PaymobResponse {
  bool success;
  String? transactionID;
  String? responseCode;
  String? message;

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

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

class PaymobPayment {
  static PaymobPayment instance = PaymobPayment();

  bool _isInitialized = 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 (_isInitialized) {
      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=';
    _isInitialized = true;
    _userTokenExpiration = userTokenExpiration;
    return _isInitialized;
  }

  /// 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 PaymobBillingData 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'];
  }

  /// Proceed to pay with only calling this function.
  /// Opens a WebView at Paymob redirectedURL to accept user payment info.
  Future<PaymobResponse?> 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(PaymobResponse 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.
      PaymobBillingData? billingData}) async {
    if (!_isInitialized) {
      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 ?? PaymobBillingData(),
    );
    if (context.mounted) {
      final response = await PaymobIFrame.show(
        context: context,
        redirectURL: _iFrameURL + purchaseToken,
        onPayment: onPayment,
      );
      return response;
    }
    return null;
  } //51624
}

class PaymobBillingData {
  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;

  PaymobBillingData({
    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 PaymobIFrame extends StatefulWidget {
  const PaymobIFrame({
    Key? key,
    required this.redirectURL,
    this.onPayment,
  }) : super(key: key);

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

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

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

class _PaymobIFrameState extends State<PaymobIFrame> {
  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('success') &&
                request.url.contains('id')) {
              final params = _getParamFromURL(request.url);
              final response = PaymobResponse.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);
    Map<String, dynamic> data = {};
    uri.queryParameters.forEach((key, value) {
      data[key] = value;
    });
    return data;
  }
}
