import Airwallex
import Foundation

@objc(AirwallexSdk)
class AirwallexSdk: RCTEventEmitter {
    private var resolve: RCTPromiseResolveBlock?
    private var reject: RCTPromiseRejectBlock?
    private var paymentConsentID: String?
    private var paymentSessionHandler: PaymentSessionHandler?
    
    @objc(initialize:enableLogging:saveLogToLocal:frameworkVersion:)
    func initialize(environment: String, enableLogging: Bool, saveLogToLocal: Bool, frameworkVersion: String) {
        if let mode = AirwallexSDKMode.from(environment) {
            Airwallex.setMode(mode)
            AWXAPIClientConfiguration.shared()
        }
        AnalyticsLogger.shared().bindExtraCommonData(["framework": "rn", "frameworkVersion": frameworkVersion])
    }
  
    @objc(presentEntirePaymentFlow:configuration:resolver:rejecter:)
    func presentEntirePaymentFlow(session: NSDictionary, configuration: NSDictionary?, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        self.resolve = resolve
        self.reject = reject

        AWXAPIClientConfiguration.shared().clientSecret = session["clientSecret"] as? String

        let session = buildAirwallexSession(from: session)
        let sheetConfiguration = AWXUIContext.Configuration(params: configuration)
        sheetConfiguration.launchStyle = .present

        DispatchQueue.main.async {
            AWXUIContext.launchPayment(
                from: self.getViewController(),
                session: session,
                paymentResultDelegate: self,
                configuration: sheetConfiguration
            )
        }
    }

    @objc(presentCardPaymentFlow:resolver:rejecter:)
    func presentCardPaymentFlow(session: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        self.resolve = resolve
        self.reject = reject
        
        AWXAPIClientConfiguration.shared().clientSecret = session["clientSecret"] as? String
        
        let session = buildAirwallexSession(from: session)
        
        DispatchQueue.main.async {
            AWXUIContext.launchCardPayment(
                from: self.getViewController(),
                session: session,
                paymentResultDelegate: self,
                launchStyle: .present
            )
        }
    }

    @objc(startApplePay:resolver:rejecter:)
    func startApplePay(session: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        self.resolve = resolve
        self.reject = reject

        AWXAPIClientConfiguration.shared().clientSecret = session["clientSecret"] as? String

        let session = buildAirwallexSession(from: session)

        DispatchQueue.main.async {
            let handler = PaymentSessionHandler(
                session: session,
                viewController: self.getViewController(),
                paymentResultDelegate: self
            )
            handler.showIndicator = false
            handler.startApplePay()
            self.paymentSessionHandler = handler
        }
    }

    @objc(payWithCardDetails:card:saveCard:resolver:rejecter:)
    func payWithCardDetails(session: NSDictionary, card: NSDictionary, saveCard: Bool, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        self.resolve = resolve
        self.reject = reject

        AWXAPIClientConfiguration.shared().clientSecret = session["clientSecret"] as? String

        let session = buildAirwallexSession(from: session)
        let card = AWXCard.decode(fromJSON: card as? [AnyHashable : Any]) as! AWXCard

        DispatchQueue.main.async {
            let handler = PaymentSessionHandler(
                session: session,
                viewController: self.getViewController(),
                paymentResultDelegate: self
            )
            handler.showIndicator = false
            handler.startCardPayment(with: card, billing: session.billing, saveCard: saveCard)
            self.paymentSessionHandler = handler
        }
    }

    @objc(payWithConsent:consent:resolver:rejecter:)
    func payWithConsent(session: NSDictionary, consent: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        self.resolve = resolve
        self.reject = reject

        AWXAPIClientConfiguration.shared().clientSecret = session["clientSecret"] as? String

        let session = buildAirwallexSession(from: session)
        let consent = AWXPaymentConsent.decode(fromJSON: consent as? [AnyHashable : Any]) as! AWXPaymentConsent

        DispatchQueue.main.async {
            let handler = PaymentSessionHandler(
                session: session,
                viewController: self.getViewController(),
                paymentResultDelegate: self
            )
            handler.showIndicator = false
            handler.startConsentPayment(with: consent)
            self.paymentSessionHandler = handler
        }
    }

    private func getViewController() -> UIViewController {
        let window = UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }

        var vc = window?.rootViewController
        while let presented = vc?.presentedViewController {
            vc = presented
        }
        return vc ?? UIViewController()
    }
}

extension AirwallexSdk: AWXPaymentResultDelegate {
    func paymentViewController(_ controller: UIViewController?, didCompleteWith status: AirwallexPaymentStatus, error: Error?) {
        switch status {
        case .success:
            var successDict = ["status": "success"]
            if let consentID = self.paymentConsentID {
                successDict["paymentConsentId"] = consentID
            }
            self.resolve?(successDict)
        case .inProgress:
            self.resolve?(["status": "inProgress"])
        case .failure:
            self.reject?((String((error as? NSError)?.code ?? -1)), error?.localizedDescription, error)
        case .cancel:
            self.resolve?(["status": "cancelled"])
        }
        resolve = nil
        reject = nil
        paymentConsentID = nil
        paymentSessionHandler = nil
    }

    func paymentViewController(_ controller: UIViewController?, didCompleteWithPaymentConsentId paymentConsentId: String) {
        self.paymentConsentID = paymentConsentId
    }
}

private extension AirwallexSDKMode {
    static func from(_ stringValue: String) -> Self? {
        switch stringValue {
        case "staging":
            .stagingMode
        case "demo":
            .demoMode
        case "production":
            .productionMode
        case "preview":
            .previewMode
        default:
            nil
        }
    }
}
