//
//  B24PaymentSdkHelper.swift
//  Bill24OnlinePaymentSdk
//
//  Created by MacbookPro on 12/10/23.
//
import Foundation
import UIKit

public class B24PaymentSdkHelper {
    var vc: BottomSheetViewController
    
    public static func frameworkBundle() -> Bundle? {
        var bundle: Bundle? = nil
        let bundlePath = Bundle(for: B24PaymentSdk.self).path(forResource: "B24PaymentSdk", ofType: "bundle")
        if(bundlePath != nil)
        {
            bundle = Bundle(path: bundlePath!)
        }else{
            bundle = Bundle(identifier: "com.bill24.B24PaymentSdk")
        }
        return bundle
    }
    
    public init (){
        // Load the storyboard
        let storyboard = UIStoryboard(name: "BottomSheet", bundle: B24PaymentSdkHelper.frameworkBundle())
        // Instantiate view controller
        vc = storyboard.instantiateViewController(withIdentifier: "BottomSheetViewController") as! BottomSheetViewController
    }
    
    public func presentBottomSheet(from controller: UIViewController, view: UIView, viewType: BottomSheetViewType, transactionId: String, completed: (() -> Void)? = nil) {
        self.vc.viewType = viewType
        self.vc.transactionId = transactionId
        self.vc.completed = completed
        
        // Configuration before view load to ensure internal layout is accurate
        self.vc.loadViewIfNeeded()
        self.vc.bottomSheetContainerView.addSubview(view)
        
        // Check if the view is a stationary view type and notify the bottom sheet
        if view is KHQRView || view is ExpireView || view is PaymentMethodView {
            self.vc.notifyStationaryViewAdded()
        }
        
        // Only set onPaymentSuccess handler for TopUp transactions
        // This prevents double presentation of success screens
        vc.onPaymentSuccess = { [weak self] data in
            // Only show WalletSuccessViewController if this is a top-up transaction
            if TopUpView.isTopUpTransaction {
                // Reset the flag immediately to prevent double-presentation
                TopUpView.isTopUpTransaction = false
                
                let storyboard = UIStoryboard(name: "InstantPaymentMethodView", bundle: B24PaymentSdkHelper.frameworkBundle())
                let walletVC = storyboard.instantiateViewController(withIdentifier: "WalletSuccessViewController") as! WalletSuccessViewController
                walletVC.modalPresentationStyle = .fullScreen
                
                if let rootVC = UIApplication.shared.windows.first?.rootViewController {
                    var topVC = rootVC
                    while let presented = topVC.presentedViewController {
                        topVC = presented
                    }
                    topVC.present(walletVC, animated: true, completion: nil)
                }
            }
        }
        
        // Use a window overlay to bypass iOS 26 (Liquid Glass) presentation chrome.
        // Any present() call — regardless of modalPresentationStyle — causes iOS 26 to inject
        // a glass backdrop panel. We add vc.view directly to the window instead.
        //
        // IMPORTANT — iOS 26 UIViewControllerHierarchyInconsistency:
        // iOS 26 added a new check: adding a VC's view directly to UIWindow while the VC has
        // a parent (set by addChild) throws UIViewControllerHierarchyInconsistency. So we
        // must NOT call addChild here. vc stays with parent == nil.
        //
        // Since vc has no parent, UIKit has no mechanism to auto-fire viewWillAppear/
        // viewDidAppear. We drive the lifecycle manually by calling them directly.
        // This is safe because we own BottomSheetViewController and both methods are
        // idempotent: viewWillAppear sets the off-screen transform, viewDidAppear starts
        // the slide-up animation.
        //
        // The dismiss() override in BottomSheetViewController checks view.superview != nil
        // (instead of parent != nil) so sheet dismissal continues to work correctly.
        let window = controller.view.window
        let targetView: UIView = window ?? controller.view

        // Clean up any previous presentation state
        if vc.parent != nil {
            vc.willMove(toParent: nil)
            vc.view.removeFromSuperview()
            vc.removeFromParent()
        } else if vc.view.superview != nil {
            vc.view.removeFromSuperview()
        }
        vc.prepareForPresentation()

        // Set frame before viewWillAppear so layoutIfNeeded() can resolve
        // sheetContainer's Auto Layout constraints (uses the view's own frame,
        // not its position in the hierarchy).
        vc.view.frame = targetView.bounds
        vc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        vc.viewWillAppear(false)       // sets sheetContainer transform off-screen
        targetView.addSubview(vc.view) // view enters window already off-screen (no flash)
        vc.viewDidAppear(false)        // starts the slide-up animation
        return
    }
    
    public static func navigationToNextBottomSheet(from viewController: UIViewController, completion: @escaping () -> Void) {
        if let currentBottomSheet = findTopViewController(from: viewController) as? BottomSheetViewController {
            // Dismiss currently presented bottom sheet if any
            currentBottomSheet.dismiss(animated: true) {
                // Call the completion block to present the new bottom sheet
                completion()
            }
        } else {
            // If no bottom sheet is currently presented, directly present the new one
            completion()
        }
    }
    
    public static func findTopViewController(from viewController: UIViewController) -> UIViewController? {
        // BottomSheetViewControllers are added directly to the window (no addChild).
        // Find by checking whether any window subview's next responder is a BottomSheetViewController.
        let window = viewController.view.window
        if let window = window {
            for subview in window.subviews.reversed() {
                if let bottomSheet = subview.next as? BottomSheetViewController {
                    return bottomSheet
                }
            }
        }
        // Fallback: walk the presented chain from the given viewController
        var topController: UIViewController? = viewController
        while let presented = topController?.presentedViewController {
            topController = presented
        }
        return topController
    }
    
    public static func errorSnackbar(view: UIView, activitiyIndicator: UIActivityIndicatorView? = nil, message: String, forBottomSheet: Bool = false){
        let snackbar = SnackbarView(frame: CGRect(x: 0, y: !forBottomSheet ? view.frame.size.height: BottomSheetViewController.bottomSheetHeight, width: view.frame.size.width, height: 80),
                                    backgroundColor:  UIColor(
                                        named: "error_snackbar_background_color",
                                        in: B24PaymentSdkHelper.frameworkBundle(),
                                        compatibleWith: nil)!)
        
        snackbar.message.text = message
        view.addSubview(snackbar)
        
        UIView.animate(withDuration: 0.3, animations: {
            snackbar.frame.origin.y -= snackbar.frame.size.height
        }) { _ in
            DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
                UIView.animate(withDuration: 0.3, animations: {
                    snackbar.frame.origin.y += snackbar.frame.size.height
                }) { _ in
                    snackbar.removeFromSuperview()
                }
            }
        }
        activitiyIndicator?.stopAnimating()
        activitiyIndicator?.isHidden = true
    }
    
    // Tag for persistent network error snackbar
    private static let persistentSnackbarTag = 9999
    private static let noInternetPopupTag = 8888
    
    
    /// Shows a persistent error snackbar that stays on screen until dismissed
    public static func persistentErrorSnackbar(view: UIView, message: String, forBottomSheet: Bool = false) {
        // Remove any existing persistent snackbar first
        removePersistentSnackbar(from: view)
        
        let snackbar = SnackbarView(frame: CGRect(x: 0, y: !forBottomSheet ? view.frame.size.height: BottomSheetViewController.bottomSheetHeight, width: view.frame.size.width, height: 80),
                                    backgroundColor:  UIColor(
                                        named: "error_snackbar_background_color",
                                        in: B24PaymentSdkHelper.frameworkBundle(),
                                        compatibleWith: nil)!)
        
        snackbar.tag = persistentSnackbarTag
        snackbar.message.text = message
        view.addSubview(snackbar)
        
        UIView.animate(withDuration: 0.3, animations: {
            snackbar.frame.origin.y -= snackbar.frame.size.height
        })
    }
    
    /// Removes the persistent error snackbar if present
    public static func removePersistentSnackbar(from view: UIView, completion: (() -> Void)? = nil) {
        guard let snackbar = view.viewWithTag(persistentSnackbarTag) else {
            completion?()
            return
        }
        
        UIView.animate(withDuration: 0.3, animations: {
            snackbar.frame.origin.y += snackbar.frame.size.height
        }) { _ in
            snackbar.removeFromSuperview()
            completion?()
        }
    }

    public static func showNoInternetPopup(on controller: UIViewController, onClose: @escaping () -> Void) {
        // Find the top-most presented controller to ensure the popup is on top of any modals
        var topController = controller
        while let presented = topController.presentedViewController {
            topController = presented
        }
        
        // Find the best view to add the popup to (prefer navigation controller's view to cover the app bar)
        let targetView: UIView = topController.navigationController?.view ?? topController.view
        
        // Ensure only one popup is shown
        if targetView.viewWithTag(noInternetPopupTag) != nil { return }
        
        let popup = NoInternetPopupView(frame: targetView.bounds)
        popup.tag = B24PaymentSdkHelper.noInternetPopupTag
        popup.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        popup.onClose = {
            onClose()
            B24PaymentSdkHelper.hideNoInternetPopup(from: controller, showSuccess: false)
        }
        
        targetView.addSubview(popup)
        
        popup.alpha = 0
        UIView.animate(withDuration: 0.3) {
            popup.alpha = 1
        }
    }
    
    /// Hides the no internet popup and optionally shows a success snackbar
    public static func hideNoInternetPopup(from controller: UIViewController, showSuccess: Bool = true) {
        // Find the top-most presented controller
        var topController = controller
        while let presented = topController.presentedViewController {
            topController = presented
        }
        
        // Look in both navigation controller's view and regular view
        let targetView: UIView = topController.navigationController?.view ?? topController.view
        guard let popup = targetView.viewWithTag(noInternetPopupTag) else { return }
        
        UIView.animate(withDuration: 0.3, animations: {
            popup.alpha = 0
        }) { _ in
            popup.removeFromSuperview()
            
            if showSuccess {
                // Show success snackbar when connection is restored
                self.successSnackbar(
                    view: controller.view,
                    message: self.localized(SnackBarLocalizedKeys.connection_restored.rawValue),
                    forBottomSheet: false
                )
            }
        }
    }

    
    public static func successSnackbar(view: UIView,  message: String, forBottomSheet: Bool = false){
//        let snackbar = SnackbarView(frame: CGRect(x: 0, y: !forBottomSheet ? view.frame.size.height : BottomSheetViewController.bottomSheetHeight, width: view.frame.size.width, height: 80), backgroundColor:  UIColor(
//            named: "success_snackbar_background_color",
//            in: B24PaymentSdkHelper.frameworkBundle(),
//            compatibleWith: nil)!
//        )
        let backgroundColor = Themes.Property.alertBackgroundColor.isEmpty ? "#EBFCF6" : Themes.Property.alertBackgroundColor
        let textColor = Themes.Property.alertTextColor.isEmpty ? "#171717" : Themes.Property.alertTextColor

        let snackbar = SnackbarView(frame: CGRect(x: 0, y: !forBottomSheet ? view.frame.size.height : BottomSheetViewController.bottomSheetHeight, width: view.frame.size.width, height: 80), backgroundColor:  UIColor(hex: backgroundColor)
        )
        
        snackbar.message.text = message
        snackbar.message.textColor = UIColor(hex: textColor)
        snackbar.iconImage.image = UIImage(named:"check-circle-48", in: B24PaymentSdkHelper.frameworkBundle(),
                                           compatibleWith: nil )
        view.addSubview(snackbar)
        UIView.animate(withDuration: 0.3, animations: {
            snackbar.frame.origin.y -= snackbar.frame.size.height
        }) { _ in
            DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
                UIView.animate(withDuration: 0.3, animations: {
                    snackbar.frame.origin.y += snackbar.frame.size.height
                }) { _ in
                    snackbar.removeFromSuperview()
                }
            }
        }
    }
    
    
    
    public static func openDeeplink(deepLinkUrl: String, view: UIView, forBottomSheet: Bool = true, completion: ((Bool) -> Void)? = nil){
        guard let deepLinkURL = URL(string: deepLinkUrl) else {
            if completion == nil {
                B24PaymentSdkHelper.errorSnackbar(
                    view: view,
                    message: B24PaymentSdkHelper.localized("msg_unable_to_open_payment"),
                    forBottomSheet: forBottomSheet
                )
            }
            completion?(false)
            return
        }
        
        UIApplication.shared.open(deepLinkURL, options: [:], completionHandler: { (success) in
            if success {
                print("Deep link opened successfully")
                completion?(true)
            } else {
                if completion == nil {
                    B24PaymentSdkHelper.errorSnackbar(
                        view: view,
                        message: B24PaymentSdkHelper.localized("msg_unable_to_open_payment"),
                        forBottomSheet: forBottomSheet
                    )
                }
                completion?(false)
            }
        })
    }
    
    public static func formateStringToDate(_ dateString: String, format: String) -> Date? {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = format
        return dateFormatter.date(from: dateString)
    }
    
    public static func formatDateToString(_ date: Date, format: String) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = format
        return dateFormatter.string(from: date)
    }
    
    // MARK: - Date Formatting Cache
    private enum DateFormatterCache {
        static let inputFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "MMM d, yyyy h:mm a"
            formatter.locale = Locale(identifier: "en_US")
            return formatter
        }()
        
        static let outputFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "dd/MM/yyyy h:mm a"
            formatter.locale = Locale(identifier: "en_US")
            return formatter
        }()
        
        static let dateOnlyFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "dd/MM/yyyy"
            return formatter
        }()
    }
    
    /// Formats transaction date from backend format to display format
    /// - Parameter dateString: Date string from backend in format "Sep 30, 2025 10:48 AM"
    /// - Returns: Formatted date string in format "dd/MM/yyyy 10:48 AM" or original string if parsing fails
    public static func formatTransactionDate(_ dateString: String) -> String {
        // Input format from backend: "Sep 30, 2025 10:48 AM"
        guard let date = DateFormatterCache.inputFormatter.date(from: dateString) else {
            // If parsing fails, return the original string
            return dateString
        }
        
        return DateFormatterCache.outputFormatter.string(from: date)
    }
    
    /// Formats transaction date to show only the date without time
    /// - Parameter dateString: Date string from backend in format "Sep 30, 2025 10:48 AM"
    /// - Returns: Formatted date string in format "dd/MM/yyyy" or original string if parsing fails
    public static func formatDateOnly(_ dateString: String) -> String {
        guard let date = DateFormatterCache.inputFormatter.date(from: dateString) else {
            return dateString
        }
        
        return DateFormatterCache.dateOnlyFormatter.string(from: date)
    }
    
    public static func setFontKhqr(named fontName: String, ofSize fontSize: CGFloat) -> UIFont? {
        if let frameworkBundle = B24PaymentSdkHelper.frameworkBundle() {
            if let fontURL = frameworkBundle.url(forResource: fontName, withExtension: "ttf") {
                if let fontDataProvider = CGDataProvider(url: fontURL as CFURL) {
                    if let font = CGFont(fontDataProvider) {
                        var error: Unmanaged<CFError>?
                        if CTFontManagerRegisterGraphicsFont(font, &error) {
                            print("Font registered successfully.")
                        } else {
                            if let error = error?.takeRetainedValue() {
                                print("Error registering font: \(error)")
                            }
                        }
                        return UIFont(name: font.fullName! as String, size: fontSize)
                    }
                }
            }
        }
        return nil
    }
    
    public static func setFont(named fontName: String, ofSize fontSize: CGFloat) -> UIFont? {
        if let frameworkBundle = B24PaymentSdkHelper.frameworkBundle() {
            if let fontURL = frameworkBundle.url(forResource: fontName, withExtension: "ttf") {
                if let fontDataProvider = CGDataProvider(url: fontURL as CFURL) {
                    if let font = CGFont(fontDataProvider) {
//                        CTFontManagerRegisterGraphicsFont(font, nil)
                        var error: Unmanaged<CFError>?
                        if CTFontManagerRegisterGraphicsFont(font, &error) {
                            print("Font \(fontName) registered successfully.")
                        } else if let error = error?.takeRetainedValue() {
                            print("Error registering font \(fontName): \(error)")
                        }
                        return UIFont(name: fontName, size: fontSize)
                    }
                }
            }
        }
        return nil
    }
    
    public static func getCurrentLanguage(language: String?){
        if(SDKVariableSetting.currentLanguage.isEmpty){
            SDKVariableSetting.currentLanguage = language?.lowercased() ?? "km"
        }
    }
    
    public static func localized(_ key: String) -> String {
        let bundle = B24PaymentSdkHelper.frameworkBundle()
        var languageBundle: Bundle?
        
        // Check if the framework bundle and the path for the language resource exist
        if let bundle = bundle, let path = bundle.path(forResource: (SDKVariableSetting.currentLanguage.isEmpty) ? "km" : SDKVariableSetting.currentLanguage.lowercased(), ofType: "lproj") {
            languageBundle = Bundle(path: path)
        }
        // If languageBundle is nil, try to use the default "km.lproj"
        if languageBundle == nil, let defaultPath = bundle?.path(forResource: "km", ofType: "lproj") {
            languageBundle = Bundle(path: defaultPath)
        }
        
        return languageBundle?.localizedString(forKey: key, value: nil, table: nil) ?? key
    }
    
}

public class NoInternetPopupView: UIView {
    
    public var onClose: (() -> Void)?
    
    private let containerView: UIView = {
        let view = UIView()
        view.backgroundColor = DefaultAppearance.shared.cardColor
        view.layer.cornerRadius = 20
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private let iconImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.image = UIImage(systemName: "wifi.slash")
        imageView.tintColor = DefaultAppearance.shared.primaryColor
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()
    
    private let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = B24PaymentSdkHelper.localized(DialogLocalizedKeys.no_internet_title.rawValue)
        label.font = FontManager.shared.boldFont(forLanguage: SDKVariableSetting.currentLanguage, fontFromconfig: DefaultAppearance.shared.fontEnglish, size: FixFontSize.cardTitle)
        label.textAlignment = .center
        label.textColor = DefaultAppearance.shared.primaryLabelColor
        return label
    }()
    
    private let messageLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = B24PaymentSdkHelper.localized(DialogLocalizedKeys.no_internet_msg.rawValue)
        label.font = FontManager.shared.regularFont(forLanguage: SDKVariableSetting.currentLanguage, fontFromconfig: DefaultAppearance.shared.fontEnglish, size: FixFontSize.contentText)
        label.textAlignment = .center
        label.numberOfLines = 0
        label.textColor = DefaultAppearance.shared.primaryLabelColor
        return label
    }()
    
    private let activityIndicator: UIActivityIndicatorView = {
        let indicator = UIActivityIndicatorView(style: .medium)
        indicator.translatesAutoresizingMaskIntoConstraints = false
        indicator.color = DefaultAppearance.shared.primaryColor
        indicator.startAnimating()
        return indicator
    }()
    
    private let waitingLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = B24PaymentSdkHelper.localized(DialogLocalizedKeys.waiting_connection.rawValue)
        label.font = FontManager.shared.regularFont(forLanguage: SDKVariableSetting.currentLanguage, fontFromconfig: DefaultAppearance.shared.fontEnglish, size: FixFontSize.tranTypeText)
        label.textAlignment = .center
        label.textColor = DefaultAppearance.shared.primaryLabelColor.withAlphaComponent(0.7)
        return label
    }()
    
    private let closeButton: UIButton = {
        let button = UIButton(type: .system)
        let config = UIImage.SymbolConfiguration(pointSize: 12, weight: .bold)
        button.setImage(UIImage(systemName: "xmark", withConfiguration: config), for: .normal)
        button.tintColor = DefaultAppearance.shared.onPrimaryColor
        button.backgroundColor = DefaultAppearance.shared.primaryColor
        button.layer.cornerRadius = 15
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }
    
    private func setupView() {
        backgroundColor = UIColor.black.withAlphaComponent(0.4)
        
        addSubview(containerView)
        containerView.addSubview(iconImageView)
        containerView.addSubview(titleLabel)
        containerView.addSubview(messageLabel)
        containerView.addSubview(activityIndicator)
        containerView.addSubview(waitingLabel)
        containerView.addSubview(closeButton)
        
        NSLayoutConstraint.activate([
            containerView.centerXAnchor.constraint(equalTo: centerXAnchor),
            containerView.centerYAnchor.constraint(equalTo: centerYAnchor),
            containerView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8),
            
            iconImageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 30),
            iconImageView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
            iconImageView.widthAnchor.constraint(equalToConstant: 80),
            iconImageView.heightAnchor.constraint(equalToConstant: 80),
            
            titleLabel.topAnchor.constraint(equalTo: iconImageView.bottomAnchor, constant: 20),
            titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20),
            titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20),
            
            messageLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
            messageLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20),
            messageLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20),
            
            activityIndicator.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 30),
            activityIndicator.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
            
            waitingLabel.topAnchor.constraint(equalTo: activityIndicator.bottomAnchor, constant: 8),
            waitingLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
            waitingLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -30),

            closeButton.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 14),
            closeButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -14),
            closeButton.widthAnchor.constraint(equalToConstant: 30),
            closeButton.heightAnchor.constraint(equalToConstant: 30)
        ])
        
        closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
    }
    
    @objc private func closeTapped() {
        onClose?()
    }
}
