//
//  BottomSheetViewController.swift
//  Bill24OnlinePaymentSdk
//
//  Created by MacbookPro on 12/10/23.
//

import Foundation
import UIKit
import Alamofire

public class BottomSheetViewController: UIViewController, UIAdaptivePresentationControllerDelegate{
    @IBOutlet weak var topBarView: UIView!
    @IBOutlet weak var bottomSheetContainerView: UIView!
    private var sheetContainer: UIView!
    private var sheetContainerTopConstraint: NSLayoutConstraint?
    private var pollingTimer: Timer?
    private var panGesture: UIPanGestureRecognizer!
    private var isLoading: Bool = false
    private var isWaitingForConnection: Bool = false
    private var isGripBarHidden: Bool = false
    private var containerTopConstraint: NSLayoutConstraint?
    
    public var completed: (() -> Void)? = nil
    var activityViewController: UIActivityViewController?
    public var shareActivityItems: [Any] = []
    
    var viewType: BottomSheetViewType = .full
    var transactionId: String = ""
    private var alreadyCalledVerify: Bool = false
    private var isSuccessViewPresented: Bool = false
    
    private var isStationaryView: Bool = false
    private var originalViewType: BottomSheetViewType = .sixtyPercent
    private var isDismissing: Bool = false
    
    public static var bottomSheetHeight: CGFloat = 0
    
    private var controller: BottomSheetViewController!
    public var onPaymentSuccess: ((Any?) -> Void)?
    public func appearanceSetup(){
        // Note: view.backgroundColor (the dim overlay) is managed by viewWillAppear/viewDidAppear only.
        // Do NOT reset it here — calling appearanceSetup() from child views (e.g. PaymentMethodView)
        // would otherwise wipe the dim that viewDidAppear already animated in.

        // Grip area should be colored
        topBarView.backgroundColor = SDKVariableSetting.isDarkMode == true ? UIColor(hex:SDKVariableSetting.indicatorDark) :UIColor(hex:SDKVariableSetting.indicatorLight)
        topBarView.layer.cornerRadius = 2.5

        // Content container should be transparent to show the seamless sheet background
        bottomSheetContainerView.backgroundColor = .clear

        let sheetBgColor = !Themes.Property.secondaryBackgroundColor.isEmpty ? UIColor(hex: Themes.Property.secondaryBackgroundColor) : UIColor(named: "primary_background_color", in: B24PaymentSdkHelper.frameworkBundle(), compatibleWith: nil)

        sheetContainer?.backgroundColor = sheetBgColor
    }
    
    public override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Force Auto Layout to resolve before applying the off-screen transform.
        // When the view is added via addChild + window.addSubview (iOS 26 chrome bypass),
        // constraints haven't been resolved yet. Without this, sheetContainer.frame is
        // zero and the slide-up animation has nothing to act on.
        view.layoutIfNeeded()

        // Start off-screen at the bottom
        let offScreenY = UIScreen.main.bounds.height
        sheetContainer.transform = CGAffineTransform(translationX: 0, y: offScreenY)

        // Start with clear background for fade-in effect
        view.backgroundColor = .clear
    }
    
    public override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // Slide up and fade in the dim overlay simultaneously
        UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut) {
            self.sheetContainer.transform = .identity
            self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        }
        
        // Re-detect KHQR view in case it was added after viewDidLoad
        detectKHQRView()
    }
    
    public override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if isBeingDismissed && completed != nil{
            completed!()
        }
        // Reset the success view flag when this view controller is being dismissed
        if isBeingDismissed {
            isSuccessViewPresented = false
            
            // Cleanup polling
            stopPolling()

            NetworkMonitor.shared.stopMonitoring()
        }
        // Re-detect KHQR view in case it was added after viewDidLoad
        detectKHQRView()
        
        NotificationCenter.default.removeObserver(self,
                                                  name: UIApplication.didBecomeActiveNotification,
                                                  object: nil)
    }
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        print("**** BottomSheet ****")
        
        // Disable iOS built-in drag-to-dismiss for pageSheet
        // We set this to true to prevent system gestures from fighting our custom panGesture.
        if #available(iOS 13.0, *) {
            isModalInPresentation = true
        }
        
        // Set presentation controller delegate
        if #available(iOS 13.0, *) {
            presentationController?.delegate = self
        }
        
        appearanceSetup()
        bottomSheetContainerView.accessibilityIdentifier = "bottomSheetContainerView"
        registerBottomSheetStyleAndConstraint()
        
        // Start monitoring network
        NetworkMonitor.shared.startMonitoring()
        setupNetworkMonitoring()
        
        startPolling()
        panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        panGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(panGesture)
        
        // Add tap gesture to dismiss when tapping outside the sheet
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundTap(_:)))
        view.addGestureRecognizer(tapGesture)
        tapGesture.cancelsTouchesInView = false

        // Check if this bottom sheet contains KHQRView
        detectKHQRView()
        
        // Register to receive the notification when back from other app
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(applicationDidBecomeActive),
                                               name: UIApplication.didBecomeActiveNotification,
                                               object: nil)
    }
    
    deinit {
        // Unregister the observer when the object is deallocated
        NotificationCenter.default.removeObserver(self,
                                                  name: UIApplication.didBecomeActiveNotification,
                                                  object: nil)
    }
    
    // Selector method that will be called when the notification is received
    @objc func applicationDidBecomeActive() {
        // Perform tasks when the application becomes active
        // Only verify if we haven't already shown success and haven't already verified
        guard NetworkMonitor.shared.isConnected else { return }
        
        if !isSuccessViewPresented && !alreadyCalledVerify {
            self.verifyTransaction(transactionId: transactionId, alreadyCalledVerify: self.alreadyCalledVerify)
        }
    }
    
    @objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
        // Prevent drag-to-close when loading OR when containing KHQR
        if isLoading || isStationaryView {
            return
        }
        
        let translation = recognizer.translation(in: view)
        let velocity = recognizer.velocity(in: view)

        switch recognizer.state {
        case .changed:
            // Allow tracking the finger, but apply resistance at the top (rubber banding)
            var yOffset = translation.y
            if yOffset < 0 {
                // Diminishing returns for pushing UP
                yOffset = -pow(-yOffset, 0.7)
            }
            sheetContainer.transform = CGAffineTransform(translationX: 0, y: yOffset)
        case .ended, .cancelled, .failed:
            // Calculate the dismiss threshold (15% of screen height)
            let screenHeight = UIScreen.main.bounds.height
            let dismissThreshold = screenHeight * 0.15
            let isQuickFlick = velocity.y > 1000 // Fast downward swipe
            
            // If is stationary view, skip dismissal (gesture should be disabled anyway)
            if isStationaryView {
                UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) {
                    self.sheetContainer.transform = .identity
                }
            } else if recognizer.state == .ended && (translation.y >= dismissThreshold || isQuickFlick) {
                // Normal dismiss behavior for non-KHQR views
                dismissBottomSheet()
            } else {
                // Snap back to home with a snappy spring
                let springVelocity = abs(velocity.y) / max(1, abs(translation.y))
                UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: springVelocity, options: [.allowUserInteraction, .beginFromCurrentState], animations: {
                    self.sheetContainer.transform = .identity
                })
            }
        default:
            break
        }
    }
    
    // MARK: - Pan Gesture Control Methods
    public func disableDragToClose() {
        panGesture?.isEnabled = false
        isLoading = true
        // Disable iOS built-in dismissal
        if #available(iOS 13.0, *) {
            isModalInPresentation = true
        }
    }
    
    public func enableDragToClose() {
        panGesture?.isEnabled = true
        isLoading = false
        // Keep isModalInPresentation = true to maintain control of dismissal
    }
    
    // Alternative methods for loading state control
    public func setLoadingState(_ loading: Bool) {
        isLoading = loading
        if #available(iOS 13.0, *) {
            isModalInPresentation = loading
        }
    }
    
    func dismissBottomSheet() {
        UIView.animate(withDuration: 0.3, animations: {
            let offScreenY = UIScreen.main.bounds.height
            self.sheetContainer.transform = CGAffineTransform(translationX: 0, y: offScreenY)
            self.view.backgroundColor = .clear
        }) { _ in
            self.dismiss(animated: false)
        }
    }

    // MARK: - Child VC dismiss override
    // The vc is added directly to the window (no addChild, no present()).
    // super.dismiss() would be a no-op. Intercept here so every existing self.dismiss()
    // call site works unchanged.
    public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        if view.superview != nil {
            if flag {
                UIView.animate(withDuration: 0.3, animations: {
                    let offScreenY = UIScreen.main.bounds.height
                    self.sheetContainer.transform = CGAffineTransform(translationX: 0, y: offScreenY)
                    self.view.backgroundColor = .clear
                }) { _ in
                    self.performChildRemoval()
                    completion?()
                }
            } else {
                performChildRemoval()
                completion?()
            }
        } else {
            super.dismiss(animated: flag, completion: completion)
        }
    }

    /// Resets state so the vc can be safely re-used after a previous presentation.
    public func prepareForPresentation() {
        isDismissing = false
        isSuccessViewPresented = false
        alreadyCalledVerify = false
    }

    private func performChildRemoval() {
        isDismissing = true
        isSuccessViewPresented = false
        stopPolling()
        NetworkMonitor.shared.stopMonitoring()
        NotificationCenter.default.removeObserver(self,
                                                  name: UIApplication.didBecomeActiveNotification,
                                                  object: nil)
        view.removeFromSuperview()
        if completed != nil { completed!() }
    }

    public func registerBottomSheetStyleAndConstraint(){
        // Create a seamless container for the sheet content
        sheetContainer = UIView()
        sheetContainer.translatesAutoresizingMaskIntoConstraints = false
        sheetContainer.layer.cornerRadius = 20
        sheetContainer.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        sheetContainer.clipsToBounds = true
        view.addSubview(sheetContainer)
        
        // Move topBar and container into the seamless sheetContainer
        topBarView.removeFromSuperview()
        bottomSheetContainerView.removeFromSuperview()
        sheetContainer.addSubview(topBarView)
        sheetContainer.addSubview(bottomSheetContainerView)
        
        topBarView.translatesAutoresizingMaskIntoConstraints = false
        bottomSheetContainerView.translatesAutoresizingMaskIntoConstraints = false
        
        // Position the container
        sheetContainerTopConstraint = sheetContainer.topAnchor.constraint(equalTo: view.topAnchor, constant: UIScreen.main.bounds.height)
        NSLayoutConstraint.activate([
            sheetContainerTopConstraint!,
            sheetContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            sheetContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            sheetContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        // Layout contents inside the container
        containerTopConstraint = bottomSheetContainerView.topAnchor.constraint(equalTo: sheetContainer.topAnchor, constant: 25)
        NSLayoutConstraint.activate([
            // Grip area at top
            topBarView.topAnchor.constraint(equalTo: sheetContainer.topAnchor, constant: 10),
            topBarView.centerXAnchor.constraint(equalTo: sheetContainer.centerXAnchor),
            topBarView.widthAnchor.constraint(equalToConstant: 40),
            topBarView.heightAnchor.constraint(equalToConstant: 5),
            
            // Content area filling the rest
            containerTopConstraint!,
            bottomSheetContainerView.leadingAnchor.constraint(equalTo: sheetContainer.leadingAnchor),
            bottomSheetContainerView.trailingAnchor.constraint(equalTo: sheetContainer.trailingAnchor),
            bottomSheetContainerView.bottomAnchor.constraint(equalTo: sheetContainer.bottomAnchor)
        ])
        
        // Update appearance
        appearanceSetup()
        
        // Apply initial detent constraints before the slide-up animation
        switch viewType {
        case .full: updateViewConstraintsForFull()
        case .ninetyPercent: updateViewConstraintsForNinetyPercent()
        case .sixtyPercent: updateViewConstraintsForSixtyPercent()
        case .thirtyPercent: updateViewConstraintsForThirtyPercent()
        case .collapsed: updateViewConstraintsForCollapsed()
        }
        
        // Call updateViewConstraints to make sure it's applied immediately
        view.setNeedsUpdateConstraints()
    }
    
    public override func updateViewConstraints() {
        switch viewType {
        case .full:
            updateViewConstraintsForFull()
        case .ninetyPercent:
            updateViewConstraintsForNinetyPercent()
        case .sixtyPercent:
            updateViewConstraintsForSixtyPercent()
        case .thirtyPercent:
            updateViewConstraintsForThirtyPercent()
        case .collapsed:
            updateViewConstraintsForCollapsed()
        }
        super.updateViewConstraints()
    }
    
    private func updateViewConstraintsForFull() {
        // Position at the top
        sheetContainerTopConstraint?.constant = 0
        
        // Calculate effective height for snackbars
        BottomSheetViewController.bottomSheetHeight = UIScreen.main.bounds.height - 80
        
        // Show the container content when not collapsed
        bottomSheetContainerView.isHidden = false
        topBarView.isHidden = isGripBarHidden
    }
    
    private func updateViewConstraintsForNinetyPercent() {
        let topPadding = UIScreen.main.bounds.height * 0.10
        sheetContainerTopConstraint?.constant = topPadding

        BottomSheetViewController.bottomSheetHeight = UIScreen.main.bounds.height * 0.90 - 80

        bottomSheetContainerView.isHidden = false
        topBarView.isHidden = isGripBarHidden
    }

    private func updateViewConstraintsForSixtyPercent() {
        let topPadding = UIScreen.main.bounds.height * 0.30

        sheetContainerTopConstraint?.constant = topPadding

        let visibleHeight = UIScreen.main.bounds.height - topPadding
        BottomSheetViewController.bottomSheetHeight = visibleHeight - 80
        
        // Show the container content when not collapsed
        bottomSheetContainerView.isHidden = false
        topBarView.isHidden = isGripBarHidden
    }
    
    private func updateViewConstraintsForThirtyPercent() {
        let topPadding = UIScreen.main.bounds.height * 0.65

        sheetContainerTopConstraint?.constant = topPadding

        let visibleHeight = UIScreen.main.bounds.height - topPadding
        BottomSheetViewController.bottomSheetHeight = visibleHeight - 80
        
        // Show the container content when not collapsed
        bottomSheetContainerView.isHidden = false
        topBarView.isHidden = isGripBarHidden
    }
    
    private func updateViewConstraintsForCollapsed() {
        let height = UIScreen.main.bounds.height * 0.06
        let top = UIScreen.main.bounds.height - height - (view.safeAreaInsets.bottom)
        
        sheetContainerTopConstraint?.constant = top
        
        BottomSheetViewController.bottomSheetHeight = top + height - 80
        
        // Hide the container content when collapsed
        bottomSheetContainerView.isHidden = true
        topBarView.isHidden = true
    }
    
    // MARK: - KHQR Specific Methods
    private func detectKHQRView() {
        // Check if any subview in bottomSheetContainerView is of a type that handles its own dismissal
        func checkForStationaryView(in view: UIView) -> Bool {
            if view is KHQRView || view is ExpireView || view is PaymentMethodView {
                return true
            }
            for subview in view.subviews {
                if checkForStationaryView(in: subview) {
                    return true
                }
            }
            return false
        }
        
        isStationaryView = checkForStationaryView(in: bottomSheetContainerView)
        if isStationaryView {
            originalViewType = viewType
            isGripBarHidden = true
            updateGripBarVisibility()
        }
    }

    private func updateGripBarVisibility() {
        topBarView.isHidden = isGripBarHidden
        containerTopConstraint?.constant = isGripBarHidden ? 15 : 25
        view.setNeedsUpdateConstraints()
        view.layoutIfNeeded()
    }
    
    // Public method to disable stationary behavior
    public func disableStationaryBehavior() {
        isStationaryView = false
        
        // Re-enable dismissal
        enableDragToClose()
    }
    
    private func expandToOriginalSize() {
        // Restore original view type
        viewType = originalViewType
        
        UIView.animate(withDuration: 0.3, animations: {
            self.updateViewConstraints()
            self.view.layoutIfNeeded()
        })
    }
    
    // Public method to be called when KHQRView is added
    public func notifyStationaryViewAdded() {
        isStationaryView = true
        originalViewType = viewType
        isGripBarHidden = true
        updateGripBarVisibility()
        
        // Disable dragging and dismissal to prevent accidental swipes
        disableDragToClose()
    }
    
    @objc private func handleBackgroundTap(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: view)
        // If tap is outside the seamless card
        if !sheetContainer.frame.contains(location) {
            // Respect stationary behavior: Ignore background taps if it's stationary
            if isStationaryView {
                return
            } else {
                dismissBottomSheet()
            }
        }
    }
    
    private func findTopBarTopConstraint() -> NSLayoutConstraint? {
        // Search in main view constraints for the one controlling topBarView.top
        for constraint in view.constraints {
            if (constraint.firstItem as? UIView) == topBarView && constraint.firstAttribute == .top {
                return constraint
            }
        }
        return nil
    }

    private func startPolling() {
        guard pollingTimer == nil else { return }
        pollingTimer = Timer.scheduledTimer(withTimeInterval: SDKVariableSetting.pollingIntervalSeconds, repeats: true) { [weak self] _ in
            guard let self = self else { return }
            if !self.isSuccessViewPresented && !self.alreadyCalledVerify {
                self.verifyTransaction(transactionId: self.transactionId, alreadyCalledVerify: self.alreadyCalledVerify)
            } else {
                self.stopPolling()
            }
        }
    }

    private func stopPolling() {
        pollingTimer?.invalidate()
        pollingTimer = nil
    }

    private func setupNetworkMonitoring() {
        NetworkMonitor.shared.onConnectionStatusChanged = { [weak self] isConnected in
            guard let self = self else { return }

            if isConnected {
                print("**** Network restored: Resuming polling ****")
                // Sync transaction state immediately when back online
                if !self.isSuccessViewPresented && !self.alreadyCalledVerify {
                    self.verifyTransaction(transactionId: self.transactionId, alreadyCalledVerify: self.alreadyCalledVerify)
                }
            } else {
                print("**** Network lost: Polling will skip until reconnected ****")
            }
        }
    }
    
   var topupValue:String = ""
    
   private func getSharePref(){
        if let topupKey = SharedPreferenceManager.getString(forKey: SharePrefKey.topup.rawValue){
            self.topupValue = topupKey
        }else{
            self.topupValue = ""
        }
    }
     
     
    
    
    private func verifyTransaction(transactionId: String, alreadyCalledVerify: Bool){
        guard NetworkMonitor.shared.isConnected else { return }
        
        if(alreadyCalledVerify == false && !isSuccessViewPresented){
            AF.request(CheckoutRouter.checkoutDetail(transactionId: transactionId)).validate().responseData{
                (response) in
                switch response.result{
                case .success(let data):
                    let transaction = try? JSONDecoder().decode(Transaction.self, from: data)
                    if(transaction?.code == "SUCCESS"){
                        if(transaction?.data?.transInfo.status == "success"){
                            
                            // Prevent multiple triggers - check if already processing
                            if self.isSuccessViewPresented {
                                return
                            }
                            
                            // Set flags immediately to prevent race conditions
                            self.isSuccessViewPresented = true
                            self.alreadyCalledVerify = true
                            
                            // Stop polling — payment confirmed
                            self.stopPolling()

                            let displayDefaultSuccessPage = transaction?.data?.checkoutPageConfig.displaySuccessPage
                            let merchantDeeplink = transaction?.data?.transInfo.redirectURL
                            
                            // Check TopUpView's static flag instead of SharedPreference
                            //open success of wallet
                            
                            if (TopUpView.isTopUpTransaction && displayDefaultSuccessPage == true){
                               
                                DispatchQueue.main.async {
//                                  guard let transaction = transaction else { return }
                                    // Call success handler on parent (safe VC)
                                    self.dismiss(animated: true) {
                                        // Only AFTER dismissal completes → trigger success
                                        self.onPaymentSuccess?(transaction)
                                    }
                                }
                                
                                // Exit early to prevent any further execution
                                return
                            }
                            
                            //open success deeplink
                           else if(displayDefaultSuccessPage == true){
                                
                                DispatchQueue.main.async {
                                    // Check one more time before presenting
                                    guard !self.isDismissing && !self.isBeingDismissed && self.presentedViewController == nil else { return }
                                    
                                    //check to open success in wallet
                                    let storyboard = UIStoryboard(name: "SuccessScreenViewController", bundle: B24PaymentSdkHelper.frameworkBundle())
                                    let vc = storyboard.instantiateViewController(withIdentifier: "SuccessScreenViewController") as! SuccessScreenViewController
                                    vc.transactionSuccess = transaction
                                    vc.presentingVC = self
                                    vc.modalPresentationStyle = .fullScreen
                                    self.present(vc, animated: true)
                                }
                                
                                // Exit early to prevent any further execution
                                return
                               
                            }else{
                                DispatchQueue.main.async {
                                    self.dismiss(animated: true){
                                        B24PaymentSdkHelper.openDeeplink(deepLinkUrl: merchantDeeplink ?? "", view: self.view)
                                    }
                                }
                                
                                // Exit early to prevent any further execution
                                return
                            }
                        }
                    }else{
//                        print("Error\(String(describing: transaction?.messageKh))")
//                        B24PaymentSdkHelper.errorSnackbar(
//                            view: self.view,
//                            message: (transaction?.messageKh ?? transaction?.message) ??  B24PaymentSdkHelper.localized(SnackBarLocalizedKeys.error.rawValue),
//                            forBottomSheet: false
//                        )
                        //let lan = SDKVariableSetting.currentLanguage?.lowercased() ?? "km"
                        let message = SDKVariableSetting.currentLanguage.lowercased() == "en" ? transaction?.message : transaction?.messageKh

                        B24PaymentSdkHelper.errorSnackbar(
                            view: self.view,
                            message: message ?? B24PaymentSdkHelper.localized(SnackBarLocalizedKeys.error.rawValue),
                            forBottomSheet: false
                        )
                    }
                case .failure(let error):
                    print(error)
                    if error.isSessionTaskError || 
                        error.asAFError?.isSessionTaskError == true ||
                        (error as NSError).code == NSURLErrorNotConnectedToInternet ||
                        (error as NSError).code == NSURLErrorTimedOut ||
                        (error as NSError).code == NSURLErrorCannotConnectToHost ||
                        (error as NSError).code == NSURLErrorNetworkConnectionLost {
                            
                            // Show popup for network errors (not snackbar)
                            if !self.isWaitingForConnection {
                                self.isWaitingForConnection = true
                                NetworkMonitor.shared.startMonitoring()
                                // Note: BottomSheet doesn't have a way to show popup directly
                                // You may want to notify child views or handle differently
                            }
                        } else {
                            // Show snackbar for non-network errors
                            B24PaymentSdkHelper.errorSnackbar(
                                view: self.view,
                                message: B24PaymentSdkHelper.localized(SnackBarLocalizedKeys.error.rawValue),
                                forBottomSheet: false
                            )
                        }
                }
            }
        }
    }
    
}

// MARK: - UIAdaptivePresentationControllerDelegate
extension BottomSheetViewController {
    public func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
        // Prevent system dismissal when loading or when containing KHQR view
        return !isLoading && !isStationaryView
    }
}








