import React
import BlazeSDK
import BlazeGAM
import GoogleMobileAds
import blaze_rtn_sdk

@objc(RTNBlazeGAM)
class BlazeGAMNativeModule: RCTEventEmitter {
    
    // This property is now a fallback only
    private let defaultAdditionalCustomGAMTargetingProperties: [String : String] = [:]
    
    override init() {
        super.init()
    }
    
    override static func moduleName() -> String {
        return "RTNBlazeGAM"
    }
    
    lazy var customNativeAdsDelegate: BlazeGAMCustomNativeAdsDelegate = .init(
        
        onGAMAdError: { [weak self] error in
            self?.onGAMAdError(error)
        },
        
        onGAMAdEvent: { [weak self] params in
            self?.onGAMAdEvent(eventType: params.eventType,
                               adData: params.adData)
        },
        
        customGAMTargetingProperties: { [weak self] params in
            return await self?.customGAMTargetingProperties(params: params) ?? [:]
        },
        
        publisherProvidedId: { [weak self] params in
            return await self?.publisherProvidedId(params: params)
        },
        
        networkExtras: { [weak self] params in
            return await self?.networkExtras(params: params)
        }
    )
    
    lazy var bannerAdsDelegate: BlazeGAMBannerAdsDelegate = .init(
        onGAMBannerAdsAdError: { [weak self] params in
            self?.onGAMBannerAdsAdError(
                error: params.error,
                adData: params.adData
            )
        },
        
        onGAMBannerAdsAdEvent: { [weak self] params in
            self?.onGAMBannerAdsAdEvent(eventType: params.eventType,
                                        adData: params.adData)
        }
    )
    
    @objc override static func requiresMainQueueSetup() -> Bool {
        return false
    }
    //------------------- Bridge methods ----------------------//
    
    @objc func enableCustomNativeAds(_ options: [String : AnyHashable],
                                     resolver: @escaping RCTPromiseResolveBlock,
                                     rejecter: @escaping RCTPromiseRejectBlock) {
        guard let options = options.extractEnableCustomNativeAdsOptions() else {
            handleError(rejecter, errMessage: "Missing params in options for `enableCustomNativeAds`")
            return
        }
        
        BlazeGAM.shared.enableCustomNativeAds(defaultCustomNativeAdsConfig: options.defaultAdConfig?.toNativeModel,
                                              delegate: customNativeAdsDelegate)
    }
    
    @objc func disableCustomNativeAds() {
        BlazeGAM.shared.disableCustomNativeAds()
    }
    
    @objc func enableBannerAds() {
        BlazeGAM.shared.enableBannerAds(delegate: bannerAdsDelegate)
    }
    
    @objc func disableBannerAds() {
        BlazeGAM.shared.disableBannerAds()
    }
    
    override func supportedEvents() -> [String] {
        return SupportedAppEvents.allCases.map({ $0.rawValue })
    }
    
}

/// Custom Native Ads implementation.
extension BlazeGAMNativeModule {
    
    func onGAMAdError(_ error: Error) {
        let event: BlazeGAMEvent = .init(name: "BlazeGAM.onAdError",
                                         body: [
                                            "errorMessage" : error.localizedDescription
                                         ])
        bridge?.sendBlazeGAMEvent(event)
    }

    func onGAMAdEvent(eventType: BlazeGoogleCustomNativeAdsHandlerEventType,
                      adData: BlazeCustomNativeAdData) {
        let event: BlazeGAMEvent = .init(name: "BlazeGAM.onAdEvent",
                                         body: [
                                            "eventType" : eventType.toReactEventTypeParam
                                         ])
        bridge?.sendBlazeGAMEvent(event)
    }
    
    func customGAMTargetingProperties(params: BlazeGAMCustomNativeAdsDelegate.RequestDataInfo) async -> [String : String] {
        guard let asyncBridge = blazeAsyncBridge else {
            return defaultAdditionalCustomGAMTargetingProperties
        }
        
        do {
            let result: [String: String] = try await asyncBridge.callJSMethod("BlazeGAM.customGAMTargetingProperties",
                                                                              params: params.toReactModel)
            return result
        } catch {
            // Log specific error and continue with fallback
            blazeLogDebug("Error customGAMTargetingProperties: \(error)")
            return self.defaultAdditionalCustomGAMTargetingProperties
        }
    }
    
    func publisherProvidedId(params: BlazeGAMCustomNativeAdsDelegate.RequestDataInfo) async -> String? {
        guard let asyncBridge = blazeAsyncBridge else {
            return nil
        }
        
        do {
            let result: String? = try await asyncBridge.callJSMethod("BlazeGAM.publisherProvidedId",
                                                                     params: params.toReactModel)
            return result
        } catch {
            // Log specific error and continue with fallback
            blazeLogDebug("Error publisherProvidedId: \(error)")
            return nil
        }
    }
    
    func networkExtras(params: BlazeGAMCustomNativeAdsDelegate.RequestDataInfo) async -> Extras? {
        guard let asyncBridge = blazeAsyncBridge else {
            return nil
        }
        
        do {
            let codableDict: [String: BlazeCodableValue] = try await asyncBridge.callJSMethod("BlazeGAM.networkExtras",
                                                                                       params: params.toReactModel)
            let additionalParameters = codableDict.anyHashableDict
            let networkExtras = Extras()
            networkExtras.additionalParameters = additionalParameters
            return networkExtras
        } catch {
            // Log specific error and continue with fallback
            blazeLogDebug("Error networkExtras: \(error)")
            return nil
        }
    }
    
}

/// Banner Ads implementation.
extension BlazeGAMNativeModule {
    
    func onGAMBannerAdsAdError(error: Error,
                               adData: BlazeGAMBannerAdsAdData) {
        let event: BlazeGAMEvent = .init(name: "BlazeGAM.onGAMBannerAdsAdError",
                                         body: [
                                            "errorMessage" : error.localizedDescription
                                         ])
        bridge?.sendBlazeGAMEvent(event)
    }

    func onGAMBannerAdsAdEvent(eventType: BlazeGAMBannerHandlerEventType,
                               adData: BlazeGAMBannerAdsAdData) {
        let event: BlazeGAMEvent = .init(name: "BlazeGAM.onGAMBannerAdsAdEvent",
                                         body: [
                                            "eventType" : eventType.toReactEventTypeParam
                                         ])
        bridge?.sendBlazeGAMEvent(event)
    }
    
}

extension BlazeGAMCustomNativeAdsDelegate.RequestDataInfo {
    
    struct RTNBlazeGAMCustomNativeAdRequestParams: Encodable {
        
        let requestDataInfo: RequestDataInfo
        
        struct RequestDataInfo: Encodable {
            let adUnitId: String
            let templateId: String
            let adContext: [String : String]
            let extraInfo: BlazeRTNContentExtraInfo
        }
        
    }
    
    var toReactModel: RTNBlazeGAMCustomNativeAdRequestParams {
        return .init(requestDataInfo: .init(adUnitId: requestDataInfo.adUnitId,
                                            templateId: requestDataInfo.templateId,
                                            adContext: requestDataInfo.adContext,
                                            extraInfo: requestDataInfo.extraInfo.toReactModel))
    }
    
}

fileprivate extension BlazeSDK.BlazeGoogleCustomNativeAdsHandlerEventType {
    
    var toReactEventTypeParam: String {
        switch self {

        case .openedAd:
            return "openedAd"

        case .adPageStart:
            return "adPageStart"

        case .adPageFirstQuarter:
            return "adPageFirstQuarter"

        case .adPageThird:
            return "adPageThird"

        case .adPageMid:
            return "adPageMid"

        case .adPageComplete:
            return "adPageComplete"

        case .pausedAdPage:
            return "pausedAdPage"

        case .resumedAdPage:
            return "resumedAdPage"

        case .ctaClicked:
            return "ctaClicked"
        }
    }
    
}

fileprivate extension BlazeSDK.BlazeGAMBannerHandlerEventType {
    
    var toReactEventTypeParam: String {
        switch self {
        case .adRequested:
            return "adRequested"
        case .adLoaded:
            return "adLoaded"
        case .adImpression:
            return "adImpression"
        case .adClicked:
            return "adClicked"
        }
    }
    
}

