//
//  BSUBanner.swift
//  UnityFramework
//
//  Created by Moin Hasan on 5/23/25.
//

import Foundation
import BlueStackSDK
import UIKit

@objc(BSUBanner)
public class BSUBanner: NSObject, BannerViewDelegate {
    @objc public var bannerClient: BSUTypeBannerClientRefPointer?
    @objc public var onBannerDidLoadCallback: BSUOnBannerDidLoadCallback?
    @objc public var onBannerDidFailedCallback: BSUOnBannerDidFailedCallback?
    @objc public var onAdClickedCallback: BSUOnAdClickedCallback?
    @objc public var onBannerDidRefreshCallback: BSUOnBannerDidRefreshCallback?
    @objc public var onBannerDidFailToRefreshCallback: BSUOnBannerDidFailToRefreshCallback?
    @objc public var onBannerHideCallback: BSUOnBannerHideCallback?
    @objc public var onBannerDisplayCallback: BSUOnBannerDisplayCallback?

    private var adPosition: BSUAdPosition = .init(rawValue: 0)!
    private var adFrame: CGRect = .zero
    private var bannerAdSize: BSUAdSize?
    private var bannerAdsFactory: BannerView?
    private var bannerView: UIView?
    private var bannerContainerView: UIView?
    private var placementId: String?

    @objc public init(bannerClientReference: BSUTypeBannerClientRefPointer?, placementId: String, adPosition: BSUAdPosition) {
        self.bannerClient = bannerClientReference
        self.adPosition = adPosition
        self.placementId = placementId
        super.init()
    }
    
    private func createBannerAd(_ adSize: String) {
        let initializeBannerFactory = {
            self.destroy()
            self.bannerAdSize = self.getBannerAdSize(from: adSize)
            self.bannerAdsFactory = BannerView(adSize: self.bannerAdSize!.size)
            self.bannerAdsFactory?.placementID = self.placementId
            self.bannerAdsFactory?.delegate = self
            self.bannerAdsFactory?.viewController = BSUPluginUtil.unityGLViewController()
//            self.bannerAdsFactory?.startAutoRefresh()
        }

        if Thread.isMainThread {
            initializeBannerFactory()
        } else {
            DispatchQueue.main.async {
                initializeBannerFactory()
            }
        }
    }

    @objc public func loadAd(withAdSize size: String) {
        createBannerAd(size)
        if Thread.isMainThread {
            self.loadBannerAd(requestOptions: nil)
        } else {
            DispatchQueue.main.async { [weak self] in
                self?.loadBannerAd(requestOptions: nil)
            }
        }
    }

    @objc public func loadAd(withAdSize size: String, requestOptions requestOptionsRef: BSUTypeRequestOptionsRef) {
        createBannerAd(size)
        let bsuOptions = Unmanaged<BSURequestOptions>.fromOpaque(requestOptionsRef).takeUnretainedValue()
        let sdkOptions = bsuOptions.toSDKRequestOptions()
        if Thread.isMainThread {
            self.loadBannerAd(requestOptions: sdkOptions)
        } else {
            DispatchQueue.main.async { [weak self] in
                self?.loadBannerAd(requestOptions: sdkOptions)
            }
        }
    }

    @objc public func show() {
        if Thread.isMainThread {
            showBannerAd()
        } else {
            DispatchQueue.main.async { [weak self] in
                self?.showBannerAd()
            }
        }
    }

    @objc public func hide() {
        if Thread.isMainThread {
            hideBannerAd()
        } else {
            DispatchQueue.main.async { [weak self] in
                self?.hideBannerAd()
            }
        }
    }

    @objc public func setPosition(_ adPosition: BSUAdPosition) {
        guard self.adPosition != adPosition else { return }
        self.adPosition = adPosition
        guard let bannerContainerView = self.bannerContainerView else { return }
        bannerContainerView.removeFromSuperview()

        guard let unityView = BSUPluginUtil.unityGLViewController()?.view else { return }
        
        unityView.addSubview(bannerContainerView)
        bannerContainerView.translatesAutoresizingMaskIntoConstraints = false
        self.setupBannerContainerViewConstraints(with: unityView,
                                                 width: self.adFrame.size.width,
                                                 height: self.adFrame.size.height)
        unityView.layoutIfNeeded()
    }

    @objc public func destroy() {
        self.removeBannerView()
        self.removeBannerContainerView()
        self.bannerAdsFactory?.delegate = nil
        self.bannerAdsFactory = nil
    }

    // MARK: - Private Methods

    private func loadBannerAd(requestOptions: RequestOptions?) {
        guard let bannerAdsFactory = self.bannerAdsFactory else { return }
        
        // Remove existing banner views if they exist
        if self.bannerContainerView != nil {
            self.removeBannerView()
            self.removeBannerContainerView()
        }
        
        // Set up new banner container
        let bannerWidth: Int = (bannerAdSize!.width == -1)
            ? Int(UIScreen.main.bounds.width)     // FULL WIDTH for dynamicBanner
            : bannerAdSize!.width

        self.adFrame = CGRect(x: 0, y: 0, width: bannerWidth, height: self.bannerAdSize!.height)
        
        self.bannerContainerView = UIView(frame: self.adFrame)
        self.bannerContainerView?.clipsToBounds = true
        
        guard let unityView = BSUPluginUtil.unityGLViewController()?.view else {
            return
        }
        
        // Add to Unity's view hierarchy
        unityView.addSubview(self.bannerContainerView!)
        self.bannerContainerView?.isHidden = true
        self.bannerContainerView?.translatesAutoresizingMaskIntoConstraints = false
        
        // Set constraints
        self.setupBannerContainerViewConstraints(with: unityView,
                                                 width: self.adFrame.size.width,
                                                 height: self.adFrame.size.height)
        unityView.layoutIfNeeded()
        
        print("BSUBanner: loadBannerAd!!")

        // Load the ad
        if requestOptions == nil {
            bannerAdsFactory.load()
        } else {
            bannerAdsFactory.load(requestOptions: requestOptions)
        }
    }

    private func hideBannerAd() {
        guard let bannerContainerView = self.bannerContainerView else { return }
        bannerContainerView.isHidden = true
//        self.bannerAdsFactory?.stopAutoRefresh()
        self.onBannerHideCallback?(self.bannerClient)
    }

    private func showBannerAd() {
        guard let bannerContainerView = self.bannerContainerView else { return }
        bannerContainerView.isHidden = false
//        self.bannerAdsFactory?.startAutoRefresh()
        self.onBannerDisplayCallback?(self.bannerClient)
    }

    private func removeBannerView() {
        guard let _ = self.bannerView else { return }

        if Thread.isMainThread {
            self.bannerView?.removeFromSuperview()
            self.bannerView = nil
        } else {
            DispatchQueue.main.async { [weak self] in
                guard let strongSelf = self else { return }
                strongSelf.bannerView?.removeFromSuperview()
                strongSelf.bannerView = nil
            }
        }
    }

    private func removeBannerContainerView() {
        guard let _ = self.bannerContainerView else { return }

        if Thread.isMainThread {
            self.bannerContainerView?.removeFromSuperview()
            self.bannerContainerView = nil
        } else {
            DispatchQueue.main.async { [weak self] in
                guard let strongSelf = self else { return }
                strongSelf.bannerContainerView?.removeFromSuperview()
                strongSelf.bannerContainerView = nil
            }
        }
    }

    private func setupBannerContainerViewConstraints(with view: UIView, width: CGFloat, height: CGFloat) {
        guard let container = bannerContainerView else { return }
        container.translatesAutoresizingMaskIntoConstraints = false

        let guide = view.safeAreaLayoutGuide // Use safe area anchors for top/bottom
        var isTopPosition: Bool { adPosition.rawValue == 0 } // 0 == top

        NSLayoutConstraint.activate([
            container.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            container.widthAnchor.constraint(equalToConstant: width),
            container.heightAnchor.constraint(equalToConstant: height),
            isTopPosition
                ? container.topAnchor.constraint(equalTo: guide.topAnchor) // guide.topAnchor for safe area
                : container.bottomAnchor.constraint(equalTo: guide.bottomAnchor) // view.bottomAnchor to ignore safe area and just bottom or top align
        ])
    }

    private func setupBannerViewConstraints(with view: UIView) {
        guard let bannerView = self.bannerView else { return }
        view.addConstraint(NSLayoutConstraint(item: bannerView,
                                              attribute: .centerX,
                                              relatedBy: .equal,
                                              toItem: view,
                                              attribute: .centerX,
                                              multiplier: 1,
                                              constant: 0))
        
        view.addConstraint(NSLayoutConstraint(item: bannerView,
                                              attribute: .centerY,
                                              relatedBy: .equal,
                                              toItem: view,
                                              attribute: .centerY,
                                              multiplier: 1,
                                              constant: 0))
    }
    
    func getBannerAdSize(from string: String?) -> BSUAdSize? {
        switch string {
        case "BANNER":
            return .banner
        case "LARGE_BANNER":
            return .largeBanner
        case "FULL_BANNER":
            return .fullBanner
        case "MEDIUM_RECTANGLE":
            return .mediumRectangle
        case "LEADERBOARD":
            return .leaderboard
        case "DYNAMIC_BANNER":
            return .dynamicBanner
        case "DYNAMIC_LEADERBOARD":
            return .dynamicLeaderboard
        default:
            return .banner
        }
    }
    
    // MARK: - BannerViewDelegate

    public func onLoad(_ bannerView: BlueStackSDK.BannerView, _ preferredHeight: CGFloat) {
//      print("Banner successfully loaded with preferredHeight: \(preferredHeight)")
        self.removeBannerView()
        self.bannerView = bannerView
        self.bannerContainerView?.addSubview(bannerView)
        self.bannerView?.translatesAutoresizingMaskIntoConstraints = false
        if let containerView = self.bannerContainerView {
            self.setupBannerViewConstraints(with: containerView)
            containerView.layoutIfNeeded()
        }
        self.onBannerDidLoadCallback?(self.bannerClient)
    }
    
    public func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) {
//      print("Banner Ad failed to load with error: \(error.localizedDescription)")
        if let nsError = error as NSError? {
            let errorPointer = Unmanaged.passUnretained(nsError).toOpaque()
            self.onBannerDidFailedCallback?(self.bannerClient, errorPointer)
        } else {
            self.onBannerDidFailedCallback?(self.bannerClient, nil)
        }
    }
    
    public func onRefresh(_ bannerView: BlueStackSDK.BannerView) {
//      print("Banner Ad refreshed")
        self.onBannerDidRefreshCallback?(self.bannerClient)
    }
    
    public func onFailedToRefresh(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) {
        print("Banner Ad failed to refresh with error: \(error.localizedDescription)")
        if let nsError = error as NSError? {
            let errorPointer = Unmanaged.passUnretained(nsError).toOpaque()
            self.onBannerDidFailToRefreshCallback?(self.bannerClient, errorPointer)
        } else {
            self.onBannerDidFailToRefreshCallback?(self.bannerClient, nil)
        }
    }
    
    public func onClick(_ bannerView: BlueStackSDK.BannerView) {
//      print("Banner Ad clicked")
        self.onAdClickedCallback?(self.bannerClient)
    }
    
    public func onResize(_ bannerView: BlueStackSDK.BannerView, _ size: CGSize) {
        print("Banner Ad resized to size: \(size)")
    }
}
