import Foundation
import BlueStackSDK
import React

@objc(BluestackBannerView)
public class BluestackBannerView: RCTView, BannerViewDelegate {
    private static let TAG = "BluestackBannerView"
    @objc public var placementId: String? {
        didSet {
            propsChanged = true
        }
    }
    @objc public var preference: String? {
        didSet {
            propsChanged = true
        }
    }
    @objc public var adSize: String? {
        didSet {
//            print("BluestackBannerView adSize: \(String(describing: adSize))")
            propsChanged = true
        }
    }
    @objc public var width: CGFloat = 0 {
        didSet {
            propsChanged = true
        }
    }
    @objc public var height: CGFloat = 0 {
        didSet {
            propsChanged = true
        }
    }
    @objc public var shouldLoadWhenReady: Bool = false {
        didSet {
//            print("BluestackBannerView shouldLoadWhenReady: \(shouldLoadWhenReady)")
            propsChanged = true
        }
    }
    @objc public var shouldLoadNow: Bool = false {
        didSet {
//            print("BluestackBannerView shouldLoadNow: \(shouldLoadNow)")
        }
    }
    
    @objc public var visible: Bool = true {
        didSet {
            updateVisibility()
        }
    }

    @objc public var onAdLoaded: RCTBubblingEventBlock?
    @objc public var onAdFailedToLoad: RCTBubblingEventBlock?
    @objc public var onAdRefreshed: RCTBubblingEventBlock?
    @objc public var onAdFailedToRefresh: RCTBubblingEventBlock?
    @objc public var onAdClicked: RCTBubblingEventBlock?
    @objc public var onAdLoadCalled: RCTBubblingEventBlock?

    private var bannerView: BannerView?
    private var adView: UIView?
    private var propsChanged: Bool = false
    private var requestOptions: RequestOptions? = nil
    private var isViewActive: Bool = false
    private var isAppActive: Bool = true
    private var eventQueue: [(Events, [String: Any]?)] = []

    public enum Events {
        case onAdLoaded
        case onAdFailedToLoad
        case onAdRefreshed
        case onAdFailedToRefresh
        case onAdClicked
        case onAdLoadCalled

        var name: String {
            switch self {
            case .onAdLoaded:
                return "onAdLoaded"
            case .onAdFailedToLoad:
                return "onAdFailedToLoad"
            case .onAdRefreshed:
                return "onAdRefreshed"
            case .onAdFailedToRefresh:
                return "onAdFailedToRefresh"
            case .onAdClicked:
                return "onAdClicked"
            case .onAdLoadCalled:
                return "onAdLoadCalled"
            }
        }

        func emit(view: BluestackBannerView, body: [String: Any]?) {
            switch self {
            case .onAdLoaded:
                view.onAdLoaded?(body)
            case .onAdFailedToLoad:
                view.onAdFailedToLoad?(body)
            case .onAdRefreshed:
                view.onAdRefreshed?(body)
            case .onAdFailedToRefresh:
                view.onAdFailedToRefresh?(body)
            case .onAdClicked:
                view.onAdClicked?(body)
            case .onAdLoadCalled:
                view.onAdLoadCalled?(body)
            }
        }
    }

    override init(frame: CGRect) {
        BlueStackLogger.debug(tag: Self.TAG, message: "BluestackBannerView init")
        isAppActive = true
        super.init(frame: frame)
        NotificationCenter.default.addObserver(self, selector: #selector(onAppDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onAppWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
    }

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

    deinit {
        BlueStackLogger.debug(tag: Self.TAG, message: "BluestackBannerView deinit")
        NotificationCenter.default.removeObserver(self)
        // destroyBanner()
    }

    @objc private func onAppDidBecomeActive() {
        isAppActive = true
        flushEvents()
    }

    @objc private func onAppWillResignActive() {
        isAppActive = false
    }

    override public func didMoveToWindow() {
        super.didMoveToWindow()
        isViewActive = (window != nil)
        if isViewActive {
            flushEvents()
        }
    }

    private func emitEvent(event: Events, body: [String: Any]? = nil) {
        if isViewActive && isAppActive {
            event.emit(view: self, body: body)
        } else {
            eventQueue.append((event, body))
        }
    }

    private func flushEvents() {
        guard isViewActive && isAppActive else { return }
        while !eventQueue.isEmpty {
            let (event, body) = eventQueue.removeFirst()
            emitEvent(event: event, body: body)
        }
    }

    private func tryInitializeSDK() {
        BluestackManager.sharedInstance?.initializeInternally(placementId: self.placementId ?? "") { [weak self] success, message in
            guard let self = self else { return }
            if success {
                self.loadBanner()
            } else {
                self.emitEvent(event: .onAdFailedToLoad, body: ["code": "INITERNAL_INIT_FAILED", "message": message])
            }
        }
    }

    func loadBanner() {
        BlueStackLogger.debug(tag: Self.TAG, message: "BluestackBannerView loadBanner")
        destroyBanner()

        if !MobileAds.sharedInstance().isInitialized() {
            tryInitializeSDK()
            return
        }

        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            
            // Initialize bannerView
    //        print("BluestackBannerView adSize: \(String(describing: self.adSize))")
            self.bannerView = BannerView(adSize: self.getAdSize(from: self.adSize)!)

            // Assign delegates
            self.bannerView?.delegate = self
            self.bannerView?.viewController = RCTPresentedViewController()
            self.bannerView?.placementID = self.placementId

            if let optionsJSON = self.preference,
               !optionsJSON.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
               optionsJSON != "{}" {
                self.requestOptions = RNRequestOptions.getRequestOptions(optionsJSON: optionsJSON)
            }

            if self.requestOptions == nil {
                self.bannerView?.load()
            } else {
                self.bannerView?.load(requestOptions: self.requestOptions)
            }
        }
    }

    @objc func create(_ preferenceJSON: String) {
        self.preference = preferenceJSON
        loadBanner()
    }

    @objc func destroy() {
        BlueStackLogger.debug(tag: Self.TAG, message: "BluestackBannerView destroy")
        destroyBanner()
    }

    @objc func refresh(_ state: Bool) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Refreshing banner view: \(state)")
            if(state){
                self.bannerView?.startAutoRefresh()
            }else{
                self.bannerView?.stopAutoRefresh()
            }
        }
    }
    
    private func updateVisibility() {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Updating visibility to: \(self.visible)")
            self.isHidden = !self.visible
        }
    }

    private func destroyBanner() {
        eventQueue.removeAll() // Clear the event queue
        if (bannerView == nil) { return }
        MainThreadDispatcher.runOnMainThreadSync {
            BlueStackLogger.debug(tag: Self.TAG, message: "Destroying banner view")
            self.bannerView?.removeFromSuperview()
            self.bannerView?.delegate = nil
            self.bannerView = nil
        }
    }

    private func createViewIfCan() {
        if (preference != nil), (placementId != nil),
           height > 0, width > -1, shouldLoadWhenReady {
            loadBanner()
        }
    }

    @objc public override func didSetProps(_ changedProps: [String]!) {
//        print("BluestackBannerView didSetProps: \(String(describing: changedProps))")
//        print("BluestackBannerView propsChanged: \(propsChanged)")
        if propsChanged && shouldLoadWhenReady{
            createViewIfCan()
        }
        propsChanged = false
    }

    func getAdSize(from string: String?) -> AdSize? {
        switch string {
        case "standard":
            return .banner
        case "large":
            return .largeBanner
        case "full":
            return .fullBanner
        case "mediumRectangle":
            return .mediumRectangle
        case "leaderboard":
            return .leaderboard
        case "dynamic":
            return .dynamicBanner
        case "dynamicLeaderboard":
            return .dynamicLeaderboardBanner
        default:
            return nil // Returns nil if no match is found
        }
    }
    
    // MARK: - BannerViewDelegate

    public func onLoad(_ bannerView: BlueStackSDK.BannerView, _ preferredHeight: CGFloat) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Banner successfully loaded with preferredHeight: \(preferredHeight)")
            // self.height = preferredHeight
            
            // remove bannerView from superview
            if bannerView.superview != nil {
                bannerView.removeFromSuperview()
            }
            
            bannerView.translatesAutoresizingMaskIntoConstraints = false
            self.addSubview(bannerView)
            
            // Set up constraints to match the banner to the container view
            NSLayoutConstraint.activate([
                bannerView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
                bannerView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
//                bannerView.widthAnchor.constraint(equalTo: self.widthAnchor),
//                bannerView.heightAnchor.constraint(equalToConstant: preferredHeight)
            ])
            
            self.emitEvent(event: .onAdLoaded, body: ["size": preferredHeight])
        }
    }
    
    public func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.error(tag: Self.TAG, message: "Banner Ad failed to load with error: \(error.localizedDescription)")
            self.emitEvent(event: .onAdFailedToLoad, body: RCTJSErrorFromNSError(error))
            self.destroyBanner()
        }
    }
    
    public func onRefresh(_ bannerView: BlueStackSDK.BannerView) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Banner Ad refreshed")
            self.emitEvent(event: .onAdRefreshed)
        }
    }
    
    public func onFailedToRefresh(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.error(tag: Self.TAG, message: "Banner Ad failed to refresh with error: \(error.localizedDescription)")
            self.emitEvent(event: .onAdFailedToRefresh, body: RCTJSErrorFromNSError(error))
        }
    }
    
    public func onClick(_ bannerView: BlueStackSDK.BannerView) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Banner Ad clicked")
            self.emitEvent(event: .onAdClicked)
        }
    }
    
    public func onResize(_ bannerView: BlueStackSDK.BannerView, _ size: CGSize) {
        MainThreadDispatcher.runOnMainThread { [weak self] in
            guard let self = self else { return }
            BlueStackLogger.debug(tag: Self.TAG, message: "Banner Ad resized to size: \(size)")
        }
    }
}
