import Foundation
import BigCrunchAds
import React
import UIKit
import GoogleMobileAds

@objc(BigCrunchBannerViewManager)
class BigCrunchBannerViewManager: RCTViewManager {

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

    override func view() -> UIView! {
        let wrapper = BigCrunchBannerViewWrapper()
        wrapper.bridge = self.bridge
        return wrapper
    }

    // Note: Property setters are handled automatically by RCT_EXPORT_VIEW_PROPERTY
    // which sets @objc properties directly on the view

    // MARK: - Command Methods

    @objc func loadAd(_ node: NSNumber) {
        DispatchQueue.main.async { [weak self] in
            guard let view = self?.bridge?.uiManager?.view(forReactTag: node) as? BigCrunchBannerViewWrapper else { return }
            view.loadAd()
        }
    }

    @objc func pauseAd(_ node: NSNumber) {
        // Pause functionality not implemented in current SDK version
    }

    @objc func resumeAd(_ node: NSNumber) {
        // Resume functionality not implemented in current SDK version
    }

    @objc func destroy(_ node: NSNumber) {
        DispatchQueue.main.async { [weak self] in
            guard let view = self?.bridge?.uiManager?.view(forReactTag: node) as? BigCrunchBannerViewWrapper else { return }
            view.destroy()
        }
    }
}

// MARK: - BigCrunchBannerViewWrapper

class BigCrunchBannerViewWrapper: UIView {

    var bannerView: BigCrunchBannerView?

    @objc var placementId: String? {
        didSet {
            if placementId != oldValue {
                setupBannerView()
                // React Native may deliver props in any order — if autoLoad arrived
                // before placementId, trigger the deferred auto-load now
                if autoLoad {
                    scheduleAutoLoad()
                }
            }
        }
    }

    // React Native sets "size" prop but we store as sizeString
    @objc var size: String {
        get { return sizeString }
        set {
            sizeString = newValue
            updateAdSize()
        }
    }

    var sizeString: String = ""  // Empty = use config sizes (no override)
    @objc var customWidth: NSNumber? {
        didSet {
            _customWidth = customWidth?.intValue
            updateAdSize()
        }
    }
    private var _customWidth: Int?

    @objc var customHeight: NSNumber? {
        didSet {
            _customHeight = customHeight?.intValue
            updateAdSize()
        }
    }
    private var _customHeight: Int?

    @objc var autoLoad: Bool = false {
        didSet {
            if autoLoad && placementId != nil {
                scheduleAutoLoad()
            }
        }
    }

    /// Single-flight auto-load: native owns autoLoad (the JS component no longer
    /// dispatches a duplicate loadAd command), and prop-order races between
    /// autoLoad and placementId must not trigger two loads.
    private var autoLoadScheduled = false

    private func scheduleAutoLoad() {
        guard !autoLoadScheduled else { return }
        autoLoadScheduled = true
        // Small delay so remaining props (size, targeting) settle before loading
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            self?.autoLoadScheduled = false
            self?.loadAd()
        }
    }

    @objc var refreshInterval: NSNumber = 0

    @objc var customTargeting: NSDictionary? {
        didSet {
            _customTargeting = customTargeting as? [String: String]
        }
    }
    private var _customTargeting: [String: String]?

    @objc var viewId: String?

    weak var bridge: RCTBridge?

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .clear
    }

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

    func setupBannerView() {
        guard let placementId = placementId else { return }

        // Remove existing banner view
        bannerView?.removeFromSuperview()
        bannerView = nil

        // Create new banner view
        let newBannerView = BigCrunchBannerView(frame: bounds)
        newBannerView.configure(placementId: placementId)
        newBannerView.delegate = self
        newBannerView.translatesAutoresizingMaskIntoConstraints = false

        addSubview(newBannerView)

        // Add constraints
        NSLayoutConstraint.activate([
            newBannerView.leadingAnchor.constraint(equalTo: leadingAnchor),
            newBannerView.trailingAnchor.constraint(equalTo: trailingAnchor),
            newBannerView.topAnchor.constraint(equalTo: topAnchor),
            newBannerView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])

        self.bannerView = newBannerView

        updateAdSize()

        if autoLoad {
            loadAd()
        }
    }

    func updateAdSize() {
        guard let bannerView = bannerView else { return }

        // If no explicit size was set (sizeString is empty and no custom dimensions),
        // don't set adSizeOverride — let the native SDK use config sizes
        if sizeString.isEmpty && _customWidth == nil && _customHeight == nil {
            bannerView.adSizeOverride = nil
            return
        }

        var googleAdSize: GoogleMobileAds.AdSize
        var bcAdSize: BCAdSize

        if let width = _customWidth, let height = _customHeight {
            bcAdSize = BCAdSize(width: Int(width), height: Int(height))
            googleAdSize = GoogleMobileAds.adSizeFor(cgSize: CGSize(width: width, height: height))
        } else {
            switch sizeString {
            case "BANNER":
                bcAdSize = BCAdSize(width: 320, height: 50)
                googleAdSize = GoogleMobileAds.AdSizeBanner
            case "LARGE_BANNER":
                bcAdSize = BCAdSize(width: 320, height: 100)
                googleAdSize = GoogleMobileAds.AdSizeLargeBanner
            case "MEDIUM_RECTANGLE":
                bcAdSize = BCAdSize(width: 300, height: 250)
                googleAdSize = GoogleMobileAds.AdSizeMediumRectangle
            case "FULL_BANNER":
                bcAdSize = BCAdSize(width: 468, height: 60)
                googleAdSize = GoogleMobileAds.AdSizeFullBanner
            case "LEADERBOARD":
                bcAdSize = BCAdSize(width: 728, height: 90)
                googleAdSize = GoogleMobileAds.AdSizeLeaderboard
            case "ADAPTIVE", "SMART":
                let frame = self.frame.width > 0 ? self.frame : UIScreen.main.bounds
                bcAdSize = BCAdSize.adaptive(width: Int(frame.width))
                googleAdSize = GoogleMobileAds.currentOrientationAnchoredAdaptiveBanner(width: frame.width)
            default:
                bcAdSize = BCAdSize(width: 320, height: 50)
                googleAdSize = GoogleMobileAds.AdSizeBanner
            }
        }

        // Set the size override on the native banner view
        bannerView.adSizeOverride = bcAdSize

        // Update frame constraints using resolved Google size for layout
        if _customWidth == nil && _customHeight == nil {
            self.frame.size = googleAdSize.size
        }
    }

    func loadAd() {
        bannerView?.loadAd()
    }

    func destroy() {
        bannerView?.destroy()
        bannerView = nil
    }

    override func removeFromSuperview() {
        destroy()
        super.removeFromSuperview()
    }

    internal override func reactViewController() -> UIViewController? {
        var responder: UIResponder? = self
        while responder != nil {
            if let viewController = responder as? UIViewController {
                return viewController
            }
            responder = responder?.next
        }
        return nil
    }

    private func sendEvent(name: String, params: [String: Any]) {
        var eventParams = params
        eventParams["viewId"] = viewId
        eventParams["placementId"] = placementId

        // Send event through RCTEventEmitter via the bridge
        guard let bridge = bridge,
              let eventEmitter = bridge.module(for: BigCrunchAdsModule.self) as? RCTEventEmitter else {
            return
        }
        eventEmitter.sendEvent(withName: name, body: eventParams)
    }
}

// MARK: - BigCrunchBannerViewDelegate

extension BigCrunchBannerViewWrapper: BigCrunchBannerViewDelegate {

    func bannerViewDidLoadAd(_ bannerView: BigCrunchBannerView) {
        sendEvent(name: "BigCrunchBannerAdLoaded", params: [:])
    }

    func bannerView(_ bannerView: BigCrunchBannerView, didFailToLoadWithError error: String) {
        sendEvent(name: "BigCrunchBannerAdFailedToLoad", params: [
            "errorCode": "LOAD_FAILED",
            "errorMessage": error
        ])
    }

    func bannerViewDidRecordImpression(_ bannerView: BigCrunchBannerView) {
        sendEvent(name: "BigCrunchBannerAdImpression", params: [:])
    }

    func bannerViewDidRecordClick(_ bannerView: BigCrunchBannerView) {
        sendEvent(name: "BigCrunchBannerAdClicked", params: [:])
    }

    // Note: The native SDK doesn't provide these events yet, but we can add them later
    // when the SDK supports them:
    // - BigCrunchBannerAdOpened
    // - BigCrunchBannerAdClosed
    // - BigCrunchBannerAdRevenue
    // - BigCrunchBannerAdViewable
}
