import os
import SwiftUI
import LinkAccount

@objc(LinkReactNativeSdkViewManager)
class LinkReactNativeSdkViewManager: RCTViewManager {

    override func view() -> (LinkReactNativeSdkView) {
        return LinkReactNativeSdkView()
    }

    @objc override static func requiresMainQueueSetup() -> Bool {
        return true
    }

    @objc func onOpenURL(_ url: String) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Notification.Name("ExternalLinkReceived"), object: url)
        }
    }
}

class LinkReactNativeSdkView: UIView {
    @objc var sessionKey: String = ""
    @objc var environment: String = ""
    @objc var redirectUrl: String = ""
    @objc var onSuccess: RCTBubblingEventBlock?
    @objc var onFailure: RCTBubblingEventBlock?
    @objc var onToggleBack: RCTBubblingEventBlock?
    @objc var onBack: RCTBubblingEventBlock?
    private static let logger = Logger(
        subsystem: Bundle.main.bundleIdentifier!,
        category: String(describing: LinkReactNativeSdkView.self)
    )
    private static var isNotificationObserver = false
    private let sessionResultExtractor = SessionResultExtractor()

    init() {
        super.init(frame: CGRect.null)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var linkEnvironment: LinkEnvironment {
        get {
            if environment == "sandbox" {
                return LinkEnvironment.sandbox
            } else {
                return LinkEnvironment.production
            }
        }
    }

    var linkController: LinkController?
    // MARK: UIView lifecycle

    override func layoutSubviews() {
        super.layoutSubviews()

        if linkController == nil {
            setupView()
        }
    }

    @objc func handleExternalLinkReceived(_ notification: Notification) {
        guard let urlString = notification.object as? String,
              let url = URL(string: urlString),
              matchesExpectedURL(url) else {
            Self.logger.warning("Link empty, invalid, or doesn't match expected URL in ExternalLinkReceived event.")
            return
        }

        Self.logger.info("Received external link \(urlString)")

        onComplete(sessionResultExtractor.extractResult(url))
        linkController?.dismiss(animated: true)
        self.removeView()
    }

    private func matchesExpectedURL(_ url: URL) -> Bool {
        guard let redirect = URL(string: self.redirectUrl) else {
            Self.logger.error("Expected Redirect URL could not be parsed as a URL object.")
            return false
        }
        if redirect.scheme != url.scheme
            || redirect.host?.lowercased() != url.host?.lowercased()
            || redirect.port != url.port
            || redirect.path != url.path {
            let redirectURLString = "\(redirect.scheme ?? "")://\(redirect.host ?? "")/\(redirect.path)"
            let urlString = "\(url.scheme ?? "")://\(url.host ?? "")/\(url.path)"
            Self.logger.warning("Path \(redirectURLString) does not match \(urlString). Port values: \(redirect.port ?? -1), \(url.port ?? -1)")
            return false
        }
        return true
    }

    private func onComplete(_ result: Result<Session, LinkError>) {
        Self.logger.info("onComplete called")
        switch result {
        case .success(let value):
            if let onSuccess = self.onSuccess {
                var paymentStatus: String = ""
                if let paymentStatusCode: PaymentStatus = value.paymentStatus {
                    paymentStatus = String(describing: paymentStatusCode).uppercased()
                }
                onSuccess([
                    "customerId": value.customerID,
                    "paymentId": value.paymentID,
                    "paymentStatus": paymentStatus
                ])
            }
        case .failure(let error):
            if let onFailure = self.onFailure {
                Self.logger.info("OnFailure called for message \(error.message) and code \(error.code)")
                onFailure(["message": error.message, "code": error.code])
            }
        }
    }

    func setupView() {
        guard let parentVC = firstAvailableUIViewController() else {
            return
        }
        Self.logger.info("Setting up LinkController")
        Self.logger.info("onBack is present: \(self.onBack != nil)")
        linkController = LinkController(
            sessionKey: sessionKey,
            redirectURL: URL(string: redirectUrl)!,
            environment: linkEnvironment,
            onClose: onBack != nil ? {
                self.onBack!(["source": "Browser Close"])
            } : nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleExternalLinkReceived),
            name: Notification.Name("ExternalLinkReceived"),
            object: nil
        )
        parentVC.addChild(linkController!)
        addSubview(linkController!.view)
        linkController?.view.frame = bounds
        linkController?.didMove(toParent: parentVC)
        try? linkController?.display()
    }

    private func removeView() {
        linkController?.willMove(toParent: nil)
        linkController?.view.removeFromSuperview()
        linkController?.removeFromParent()
        linkController = nil
        NotificationCenter.default.removeObserver(self)
    }

    private func firstAvailableUIViewController() -> UIViewController? {
        var responder = next
        while responder != nil {
            if responder is UIViewController {
                return responder as? UIViewController
            }
            responder = responder?.next
        }

        return nil
    }
}
