import Foundation
import UIKit
import React
import ComplyCubeMobileSDK

@objc(ComplyCubeRNSDK)
final class ComplyCubeRNSDK: RCTEventEmitter, ComplyCubeMobileSDKDelegate {
    private let eventTelemetry = "ComplyCubeEvent"
    private let eventCustom = "ComplyCubeCustomEvent"
    private let eventSuccess = "ComplyCubeSuccess"
    private let eventError = "ComplyCubeError"
    private let eventCancel = "ComplyCubeCancel"

    private func traceCallback(_ name: String, payload: Any? = nil) {
        if let payload {
            NSLog("[ComplyCubeRN][callback] %@ payload=%@", name, String(describing: payload))
        } else {
            NSLog("[ComplyCubeRN][callback] %@", name)
        }
    }

    private func nowMillis() -> Int64 {
        Int64(Date().timeIntervalSince1970 * 1000)
    }

    private func emitEvent(_ name: String, body: Any) {
        let emitBlock = {
            self.sendEvent(withName: name, body: body)
            CCLog.info("ComplyCube RN event emitted", ["name": name, "hasJSListeners": self.hasJSListeners])
            self.traceCallback("emit:\(name)", payload: [
                "hasJSListeners": self.hasJSListeners,
                "body": body
            ])
        }

        if Thread.isMainThread {
            emitBlock()
        } else {
            DispatchQueue.main.async(execute: emitBlock)
        }
    }

    private func emitLifecycle(type: String, payload: Any) {
        let envelope: [String: Any] = [
            "type": type,
            "payload": payload,
            "ts": nowMillis()
        ]
        emitEvent(eventTelemetry, body: envelope)
        emitEvent(eventCustom, body: envelope)
    }

    func onError(_ errors: [ComplyCubeError]) {
        let details: [[String: Any]] = errors.map {
            [
                "message": $0.message,
                "errorCode": $0.errorCode.rawValue,
                "description": $0.description
            ]
        }
        let message = errors.first?.message ?? "ComplyCube SDK error"
        let payload: [String: Any] = [
            "message": message,
            "errors": details
        ]
        CCLog.error("ComplyCube onError (\(errors.count) error(s))", payload)
        traceCallback("onError", payload: payload)
        emitEvent(eventError, body: payload)
        emitLifecycle(type: "error", payload: payload)
        let nativeError = NSError(
            domain: "ComplyCubeRNSDK",
            code: 1,
            userInfo: [
                NSLocalizedDescriptionKey: message,
                "errors": details
            ]
        )
        self.finishError(code: "E_SDK", message: message, error: nativeError)

        // Maintain your "critical" behavior.
        let isCritical = errors.contains { e in
            switch e.errorCode {
            case .expiredToken, .jailbroken: return true
            default: return false
            }
        }
        if isCritical {
            CCLog.warn("ComplyCube critical error – dismissing VC")
//            presentingViewController?.dismiss(animated: true, completion: nil)
        }
    }
    
    func onCancelled(_ error: ComplyCubeError) {
        let payload: [String: Any] = [
            "message": error.message,
            "errorCode": error.errorCode.rawValue,
            "description": error.description
        ]
        CCLog.warn("ComplyCube onCancelled", payload)
        traceCallback("onCancelled", payload: payload)
        emitEvent(eventCancel, body: payload)
        emitLifecycle(type: "cancelled", payload: payload)
        self.finishCancel(error.message)
    }
    
    func onSuccess(_ result: ComplyCubeResult) {
        let payload: [String: Any] = [
            "liveVideoId": result.liveVideoId ?? NSNull(),
            "livePhotoId": result.livePhotoId ?? NSNull(),
            "ids": result.ids.map { String(describing: $0) }
        ]
        CCLog.info("ComplyCube onSuccess(Result)", payload)
        traceCallback("onSuccess(Result)", payload: payload)
        emitEvent(eventSuccess, body: payload)
        emitLifecycle(type: "success", payload: payload)
        self.finishSuccess(payload)
    }
    
    func onSuccess(_ result: ComplyCubeIDResult) {
        let payload: [String: Any] = ["ids": result.itemList.map { String(describing: $0) }]
        CCLog.info("ComplyCube onSuccess(IDResult)", payload)
        traceCallback("onSuccess(IDResult)", payload: payload)
        emitEvent(eventSuccess, body: payload)
        emitLifecycle(type: "success", payload: payload)
        self.finishSuccess(payload)
    }
    
    
    // MARK: Event stream
    private var hasJSListeners = false
    override static func requiresMainQueueSetup() -> Bool { true }
    @objc override func addListener(_ eventName: String!) {
        super.addListener(eventName)
        hasJSListeners = true
        traceCallback("addListener", payload: ["eventName": eventName as Any, "hasJSListeners": hasJSListeners])
    }
    @objc override func removeListeners(_ count: Double) {
        super.removeListeners(count)
        traceCallback("removeListeners", payload: ["count": count, "hasJSListeners": hasJSListeners])
    }
    override func startObserving() {
        hasJSListeners = true
        traceCallback("startObserving", payload: ["hasJSListeners": hasJSListeners])
    }
    override func stopObserving()  {
        hasJSListeners = false
        traceCallback("stopObserving", payload: ["hasJSListeners": hasJSListeners])
    }
    override func supportedEvents() -> [String]! {
        [eventTelemetry, eventCustom, eventSuccess, eventError, eventCancel]
    }
    
    // MARK: One-shot promise state
    private var resolve: RCTPromiseResolveBlock?
    private var reject: RCTPromiseRejectBlock?
    private var finished = false
    private let deps = ComplyCubeConfigurationFactory().makeDependencies()
    
    private func finishSuccess(_ result: [String: Any]) {
        guard !finished else { return }
        finished = true
        traceCallback("finishSuccess", payload: result)
        resolve?(result)
        resolve = nil; reject = nil
    }
    private func finishCancel(_ message: String = "User cancelled") {
        guard !finished else { return }
        finished = true
        traceCallback("finishCancel", payload: ["message": message])
        reject?("E_CANCELLED", message, nil)
        resolve = nil; reject = nil
    }
    private func finishError(code: String = "E_SDK", message: String, error: Error? = nil) {
        guard !finished else { return }
        finished = true
        traceCallback("finishError", payload: ["code": code, "message": message, "error": String(describing: error)])
        reject?(code, message, error)
        resolve = nil; reject = nil
    }
    
    // MARK: start(options) → Promise
    @objc(start:resolver:rejecter:)
    func start(_ options: NSDictionary,
               resolver: @escaping RCTPromiseResolveBlock,
               rejecter: @escaping RCTPromiseRejectBlock) {
        DispatchQueue.main.async {
            let optionKeys = (options as? [String: Any])?.map { $0.key } ?? []
            let workflowTemplateId = (options as? [String: Any])?["workflowTemplateId"] as? String
            let stageCount = ((options as? [String: Any])?["stages"] as? [Any])?.count ?? 0
            self.traceCallback("start:invoked", payload: [
                "hasJSListeners": self.hasJSListeners,
                "optionKeys": optionKeys,
                "workflowTemplateId": workflowTemplateId as Any,
                "stageCount": stageCount
            ])
            // Prevent concurrent sessions
            if self.resolve != nil && self.finished == false {
                rejecter("E_BUSY", "A verification session is already in progress.", nil)
                return
            }
            
            self.resolve = resolver
            self.reject = rejecter
            self.finished = false
            
            do {
                let settings = try ComplyCubeSettings.make(from: options as! [String : Any], deps: self.deps)
                NSLog("[CCViewController] done: Stages Count=%d", settings.stages.count)
                NSLog("[CCViewController] done: Stages WorkflowId= %@", String(describing: settings.workflowTemplateId))
                if let wf = settings.workflowTemplateId, !wf.isEmpty {
                    self.traceCallback("start:path", payload: ["mode": "workflow", "workflowId": wf])
                    let flow = ComplyCubeMobileSDK.FlowBuilder()
                        .withSDKToken(settings.clientToken)
                        .withClientId(settings.clientID)
                        .withWorkflowId(wf)
                        .withEventHandler { (event: Event) in
                            let data: [String: Any] = ["event": event.rawValue]
                            CCLog.info("ComplyCube Event", data)
                            self.traceCallback("event", payload: data)
                            self.emitLifecycle(type: "telemetry", payload: data)
                        }
                        .withCallbackHandler(self)
                    if let scheme = settings.colorScheme { _ = flow.withColorScheme(scheme) }
                    if let look   = settings.lookAndFeel  { _ = flow.withLookAndFeel(look) }
                    try flow.start()
                } else {
                    self.traceCallback("start:path", payload: ["mode": "stages", "count": settings.stages.count])
                    let flow = ComplyCubeMobileSDK.FlowBuilder()
                        .withEventHandler { (event: Event) in
                            CCLog.info("ComplyCube Event", ["event": event.rawValue])
                            self.traceCallback("event", payload: ["event": event.rawValue])
                            self.emitLifecycle(type: "telemetry", payload: ["event": event.rawValue])
                        }
                        .withCallbackHandler(self)
                        .withClientId(settings.clientID)
                        .withSDKToken(settings.clientToken)
                        .withStages(settings.stages)
                    if let scheme = settings.colorScheme { _ = flow.withColorScheme(scheme) }
                    if let look   = settings.lookAndFeel  { _ = flow.withLookAndFeel(look) }
                    try flow.start()
                }
            } catch {
                CCLog.error("ComplyCube settings build failed", ["error": String(describing: error)])
                self.emitEvent(self.eventError, body: [
                    "message": "Failed to build ComplyCube settings",
                    "error": String(describing: error)
                ])
                self.emitLifecycle(type: "error", payload: [
                    "message": "Failed to build ComplyCube settings",
                    "error": String(describing: error)
                ])
                self.finishError(code: "E_SDK", message: "Failed to build ComplyCube settings", error: error)
                //        self.presentingViewController?.dismiss(animated: true, completion: nil)
            }
        }
    }
}
