import Foundation
import Capacitor
import IdensicMobileSDK

/**
 * Please read the Capacitor iOS Plugin Development Guide
 * here: https://capacitorjs.com/docs/plugins/ios
 */
@objc(SumsubMobileSdkPlugin)
public class SumsubMobileSdkPlugin: CAPPlugin, CAPBridgedPlugin {
    public let identifier = "SumsubMobileSdkPlugin"
    public let jsName = "SumsubMobileSdk"
    public let pluginMethods: [CAPPluginMethod] = [
        CAPPluginMethod(name: "initialize", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "present", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "dismiss", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "setAccessToken", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "getVerificationStatus", returnType: CAPPluginReturnPromise)
    ]
    
    private let implementation = SumsubMobileSdk()
    private var tokenExpirationCallback: ((String?) -> Void)?
    
    @objc func initialize(_ call: CAPPluginCall) {
        guard let accessToken = call.getString("accessToken") else {
            call.reject("Access token is required")
            return
        }

        let applicantConfig = call.getObject("applicantConfig")
        let email = applicantConfig?["email"] as? String
        let phone = applicantConfig?["phone"] as? String
        let debug = call.getBool("debug") ?? false
        let locale = call.getString("locale")
        let isAnalyticsEnabled = call.getBool("isAnalyticsEnabled") ?? true
        
        implementation.initialize(accessToken: accessToken, plugin: self, email: email, phone: phone, debug: debug, locale: locale, isAnalyticsEnabled: isAnalyticsEnabled)
        call.resolve(["status": mapStatus(implementation.getVerificationStatus())])
    }

    @objc func present(_ call: CAPPluginCall) {
        guard let viewController = self.bridge?.viewController else {
            call.reject("No view controller available")
            return
        }

        guard implementation.isInitialized else {
            call.reject("SDK not initialized")
            return
        }
        
        let dismissalTimeInterval = call.getDouble("dismissalTimeInterval") ?? 0
        DispatchQueue.main.async {
            self.implementation.present(from: viewController, completion: { error in
                if let error = error {
                    call.reject("Error presenting SDK: \(error.localizedDescription)")
                } else {
                    call.resolve()
                }
            }, dismissalTimeInterval: dismissalTimeInterval)
        }
    }
    
    @objc func dismiss(_ call: CAPPluginCall) {
        DispatchQueue.main.async { [weak self] in
            self?.implementation.dismiss()
            call.resolve()
        }
    }
    
    func requestNewToken(_ completion: @escaping (String?, Error?) -> Void) {
        tokenExpirationCallback = { token in
            completion(token, nil)
        }
        
        notifyListeners("onTokenExpired", data: [:])
    }

    @objc func setAccessToken(_ call: CAPPluginCall) {
        guard let token = call.getString("token") else {
            call.reject("No token provided")
            tokenExpirationCallback?(nil)
            return
        }
        
        tokenExpirationCallback?(token)
        call.resolve()
    }

    @objc func getVerificationStatus(_ call: CAPPluginCall) {
        guard implementation.isInitialized else {
            call.resolve(["status": "NotInitialized"])
            return
        }
        
        let status = implementation.getVerificationStatus()
        call.resolve(["status": mapStatus(status)])
    }
    
    func notifyStatusChanged(prevStatus: SNSMobileSDK.Status, newStatus: SNSMobileSDK.Status, statusDescription: String, verboseStatus: String, failReason: String) {
        let statusInfo: [String: String] = [
            "prevStatus": mapStatus(prevStatus),
            "newStatus": mapStatus(newStatus),
            "statusDescription": statusDescription,
            "verboseStatus": verboseStatus,
            "failReason": failReason,
        ]
        
        notifyListeners("onVerificationStatusChanged", data: statusInfo)
    }

    func notifyFailStatus(_ failReason: String) {
        notifyListeners("onVerificationFailed", data: ["failReason": failReason])
    }
    
    func notifyApprovalStatus(_ isApproved: Bool) {
        notifyListeners("onApprovedStatusChanged", data: ["isApproved": isApproved])
    }

    func notifyApplicantLoaded(_ applicantId: String) {
        notifyListeners("onApplicantLoaded", data: ["applicantId": applicantId])
    }

    func notifyStepInitiated(_ step: String) {
        notifyListeners("onStepInitiated", data: ["step": step])
    }

    func notifyStepCompleted(step: String, isCancelled: Bool) {
        notifyListeners("onStepCompleted", data: ["step": step, "isCancelled": isCancelled])
    }

    func notifyAnalyticsEvent(eventName: String, payload: [String: Any]) {
        notifyListeners("onAnalyticsEvent", data: ["eventName": eventName, "payload": payload])
    }

    func notifyEvent(eventName: String, payload: [String: Any]) {
        notifyListeners("onEvent", data: ["eventName": eventName, "payload": payload])
    }

    func notifyDidDismiss(status: SNSMobileSDK.Status, description: String) {
        notifyListeners("onDidDismiss", data: ["status": status, "description": description])
    }

    func logHandler(level: SNSLogLevel, message: String) {
        let logLevel: String
        switch level {
            case .off:
                logLevel = "OFF"
            case .error:
                logLevel = "ERROR"
            case .warning:
                logLevel = "WARNING"
            case .info:
                logLevel = "INFO"
            case .debug:
                logLevel = "DEBUG"
            case .trace:
                logLevel = "TRACE"
            @unknown default:
                logLevel = "UNKNOWN"
        }
        
        notifyListeners("onLog", data: ["level": logLevel, "message": "\(Date()) [\(logLevel)] \(message)"])
    }
    
    // Map the SDK statuses to string values that match our TypeScript enum
    func mapStatus(_ status: SNSMobileSDK.Status) -> String {
        switch status {
        case .ready:
            return "Ready"
        case .initial:
            return "Initial"
        case .failed:
            return "Failed"
        case .incomplete:
            return "Incomplete"
        case .pending:
            return "Pending"
        case .temporarilyDeclined:
            return "TemporarilyDeclined"
        case .finallyRejected:
            return "FinallyRejected"
        case .approved:
            return "Approved"
        @unknown default:
            return "Unknown"
        }
    }
}
