import Foundation
import UIKit
import React
import Qualtrics

@objc(RNQualtricsDigitalImpl)
class RNQualtricsDigitalImpl: NSObject {
    
    // MARK: - Properties
    
    private var gTargetingResult: TargetingResult?
    private var gInitializationResult: InitializationResult?
    
    // Event names
    private let INITIALIZE_EVENT = "initializeEvent"
    private let EVALUATE_EVENT = "evaluateEvent"
    private let INITIALIZE_PROJECT_EVENT = "initializeProjectEvent"
    private let EVALUATE_PROJECT_EVENT = "evaluateProjectEvent"
    private let EVALUATE_INTERCEPT_EVENT = "evaluateInterceptEvent"
    
    // Event emitter
    private weak var eventEmitter: RCTEventEmitter?
    
    // MARK: - Setup
    
    @objc
    func setEventEmitter(_ eventEmitter: RCTEventEmitter) {
        self.eventEmitter = eventEmitter
    }
    
    // MARK: - API Methods
    
    @objc(initialize:zoneId:interceptId:)
    func initialize(_ brandId: String, zoneId: String, interceptId: String) {
        Qualtrics.shared.initialize(brandId: brandId, zoneId: zoneId, interceptId: interceptId, completion: nil);
    }
    
    @objc(initializeWithCompletion:zoneId:interceptId:)
    func initializeWithCompletion(_ brandId: String, zoneId: String, interceptId: String) {
        Qualtrics.shared.initialize(brandId: brandId, zoneId: zoneId, interceptId: interceptId) { [weak self] initializationResult in
            guard let self = self else { return }
            
            var initializeResult: [String: Any]?
            if initializationResult != nil {
                self.gInitializationResult = initializationResult
                initializeResult = [
                    "passed": initializationResult.passed(),
                    "message": initializationResult.getMessage()
                ]
                self.sendEvent(withName: self.INITIALIZE_EVENT, body: initializeResult)
            } else {
                self.sendEvent(withName: self.INITIALIZE_EVENT, body: NSNull())
            }
        }
    }
    
    @objc(initializeProject:zoneId:extRefId:)
    func initializeProject(_ brandId: String, zoneId: String, extRefId: String?) {
        var initializationResults = [String: [String: Any]]()
        
        Qualtrics.shared.initializeProject(brandId: brandId, projectId: zoneId, extRefId: extRefId) { [weak self] result in
            guard let self = self else { return }
            
            for (key, initializationResult) in result {
                if initializationResult.getMessage == nil {
                    continue
                }
                let initializeResult: [String: Any] = [
                    "passed": initializationResult.passed(),
                    "message": initializationResult.getMessage()
                ]
                initializationResults[key] = initializeResult
            }
            for (key, value) in initializationResults {
                print("Qualtrics: \(key) = \(value)")
            }
            self.sendEvent(withName: self.INITIALIZE_PROJECT_EVENT, body: initializationResults)
        }
    }
    
    @objc(evaluateTargetingLogic)
    func evaluateTargetingLogic() {
        var targetResult: [String: Any]?
        let initializeFailed = gInitializationResult != nil && !gInitializationResult!.passed()
        if initializeFailed {
            targetResult = [
                "passed": gInitializationResult!.passed(),
                "surveyUrl": NSNull()
            ]

            sendEvent(withName: EVALUATE_EVENT, body: targetResult)
            return
        }
        
        Qualtrics.shared.evaluateTargetingLogic { [weak self] targetingResult in
            guard let self = self else { return }
            
            if targetingResult != nil {
                self.gTargetingResult = targetingResult

                targetResult = [
                    "passed": targetingResult.passed(),
                    "surveyUrl": targetingResult.getSurveyUrl() ?? NSNull()
                ]
                
                self.sendEvent(withName: self.EVALUATE_EVENT, body: targetResult)
            } else {
                self.sendEvent(withName: self.EVALUATE_EVENT, body: NSNull())
            }
        }
    }
    
    @objc(evaluateProject)
    func evaluateProject() {
        var targetingResults = [String: [String: Any]]()
        
        Qualtrics.shared.evaluateProject { [weak self] result in
            guard let self = self else { return }
            
            if result != nil {
                for (key, targetingResult) in result {
                    let targetResult: [String: Any] = [
                        "passed": targetingResult.passed(),
                        "surveyUrl": targetingResult.getSurveyUrl() ?? NSNull(),
                        "creativeType": targetingResult.getCreativeType() ?? NSNull()
                    ]
                    targetingResults[key] = targetResult
                }
                
                self.sendEvent(withName: self.EVALUATE_PROJECT_EVENT, body: targetingResults)
            }
        }
    }
    
    @objc(evaluateIntercept:)
    func evaluateIntercept(_ interceptId: String) {
        var targetResult: [String: Any]?
        
        Qualtrics.shared.evaluateIntercept(for: interceptId) { [weak self] targetingResult in
            guard let self = self else { return }
            
            if targetingResult != nil {
                self.gTargetingResult = targetingResult
                
                targetResult = [
                    "passed": targetingResult.passed(),
                    "surveyUrl": targetingResult.getSurveyUrl() ?? NSNull(),
                    "creativeType": targetingResult.getCreativeType() ?? NSNull()
                ]
                
                self.sendEvent(withName: self.EVALUATE_INTERCEPT_EVENT, body: targetResult)
            } else {
                self.sendEvent(withName: self.EVALUATE_INTERCEPT_EVENT, body: NSNull())
            }
        }
    }
    
    @objc(display:callback:)
    func display(_ autoCloseSurvey: Bool, callback: @escaping RCTResponseSenderBlock) {
        let initializeFailed = gInitializationResult != nil && !gInitializationResult!.passed()
        if initializeFailed {
            callback([false])
            return
        }
        
        var rootViewController = UIApplication.shared.delegate?.window??.rootViewController
        
        if rootViewController == nil {
            var rootWindow: UIWindow?
            let windows = UIApplication.shared.windows
            
            for window in windows {
                if window.isKeyWindow {
                    rootWindow = window
                    break
                }
            }
            rootViewController = rootWindow?.rootViewController
        }
        
        if rootViewController == nil {
            print("Qualtrics: Unable to find the app's rootViewController")
            callback([false])
            return
        }
        
        let displayResult = Qualtrics.shared.display(viewController: rootViewController!, autoCloseSurvey: NSNumber(value: autoCloseSurvey))
        
        if displayResult {
            gTargetingResult = nil
        }

        callback([displayResult])
    }
    
    @objc(displayTarget:autoCloseSurvey:callback:)
    func displayTarget(_ surveyUrl: String?, autoCloseSurvey: NSNumber?, callback: @escaping RCTResponseSenderBlock) {
        let closeSurvey = autoCloseSurvey?.boolValue ?? false
        
        var rootViewController = UIApplication.shared.delegate?.window??.rootViewController
        var displayTargetResult = false
        
        if rootViewController == nil {
            var rootWindow: UIWindow?
            let windows = UIApplication.shared.windows
            
            for window in windows {
                if window.isKeyWindow {
                    rootWindow = window
                    break
                }
            }
            rootViewController = rootWindow?.rootViewController
        }
        
        if rootViewController == nil {
            print("Qualtrics: Error unable to find the app's rootViewController")
            callback([false])
            return
        }
        
        do {
            // If no surveyURL provided, attempt V1 displayTarget workflow using gTargetingResult
            if (surveyUrl?.isEmpty ?? true) && gTargetingResult != nil {
                if let targetUrl = gTargetingResult!.getSurveyUrl() {
                    Qualtrics.shared.displayTarget(targetViewController: rootViewController!, targetUrl: targetUrl, autoCloseSurvey: NSNumber(value: closeSurvey))
                    displayTargetResult = true
                }
            }
            // Check that surveyUrl is provided and not null/empty, attempt V2 displayTarget workflow
            else if let surveyUrl = surveyUrl, !surveyUrl.isEmpty {
                Qualtrics.shared.displayTarget(targetViewController: rootViewController!, targetUrl: surveyUrl, autoCloseSurvey: NSNumber(value: closeSurvey))
                displayTargetResult = true
            }
            // If not V1 or V2 compatible, log error to stderr
            else {
                print("Qualtrics: Error when trying to display target: provided surveyUrl was null or empty.")
            }
        }
        catch {
            print("Qualtrics: Error when trying to display target: \(error.localizedDescription)")
        }

        callback([displayTargetResult])
    }
    
    @objc(displayIntercept:autoCloseSurvey:callback:)
    func displayIntercept(_ interceptId: String, autoCloseSurvey: NSNumber, callback: @escaping RCTResponseSenderBlock) {
        let closeSurvey = autoCloseSurvey.boolValue

        var rootViewController = UIApplication.shared.delegate?.window??.rootViewController
        
        if rootViewController == nil {
            var rootWindow: UIWindow?
            let windows = UIApplication.shared.windows
            
            for window in windows {
                if window.isKeyWindow {
                    rootWindow = window
                    break
                }
            }
            rootViewController = rootWindow?.rootViewController
        }
        
        if rootViewController == nil {
            print("Qualtrics: Unable to find the app's rootViewController")
            callback([false])
            return
        }
        
        let displayResult = Qualtrics.shared.displayIntercept(for: interceptId, viewController: rootViewController!, autoCloseSurvey: NSNumber(value: closeSurvey))
        
        callback([displayResult])
    }
    
    @objc(setString:value:)
    func setString(_ key: String, value: String) {
        Qualtrics.shared.properties.setString(string: value, for: key)
    }
    
    @objc(setNumber:value:)
    func setNumber(_ key: String, value: Double) {
        Qualtrics.shared.properties.setNumber(number: value, for: key)
    }
    
    @objc(setDateTime:)
    func setDateTime(_ key: String) {
        Qualtrics.shared.properties.setDateTime(for: key)
    }
    
    @objc(setLastDisplayTimeForIntercept:)
    func setLastDisplayTimeForIntercept(_ interceptId: String) {
        Qualtrics.shared.properties.setLastDisplayTimeForIntercept(for: interceptId)
    }
    
    @objc(setNotificationIconAsset:)
    func setNotificationIconAsset(_ asset: String) {
        print("iOS uses the App icon for all notifications, icon cannot be set.")
    }
    
    @objc(registerViewVisit:)
    func registerViewVisit(_ viewName: String) {
        Qualtrics.shared.registerViewVisit(viewName: viewName)
    }
    
    @objc(resetTimer)
    func resetTimer() {
        Qualtrics.shared.resetTimer()
    }
    
    @objc(resetViewCounter)
    func resetViewCounter() {
        Qualtrics.shared.resetViewCounter()
    }
    
    @objc(recordClick)
    func recordClick() {
        gTargetingResult?.recordClick()
    }
    
    @objc(recordImpression)
    func recordImpression() {
        gTargetingResult?.recordImpression()
    }
    
    @objc(getInitializedIntercepts:)
    func getInitializedIntercepts(_ callback: @escaping RCTResponseSenderBlock) {
        let intercepts = Qualtrics.shared.getInitializedIntercepts()
        callback([intercepts])
    }
    
    @objc(getPassingIntercepts:)
    func getPassingIntercepts(_ callback: @escaping RCTResponseSenderBlock) {
        let intercepts = Qualtrics.shared.getPassingIntercepts()
        callback([intercepts])
    }
    
    // MARK: - Helper Methods
    
    private func sendEvent(withName name: String, body: Any?) {
        eventEmitter?.sendEvent(withName: name, body: body)
    }
}
