import BlazeSDK
import Foundation
import React

// Represents error code that can be converted into a BlazeError on the React Native side.
let blazeConvertibleErrorCode = "BLAZE_CONVERTIBLE_ERROR"

// Represents a generic error code on the Blaze React Native side.
let blazeReactNativeErrorCode = "BLAZE_REACT_NATIVE_ERROR"

extension BlazeResult {

    public func handleResult(
        resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        switch self {
        case .success(let value):
            resolver(value)
        case .failure(let blazeError):
            let error: NSError?
            if let reactNativeErrorString = blazeError.toReactValue {
                error = NSError(
                    domain: blazeConvertibleErrorCode,
                    code: 0,
                    userInfo: [
                        NSLocalizedDescriptionKey: blazeError.localizedDescription,
                        "details": reactNativeErrorString,
                    ])
            } else {
                error = nil
            }
            
            rejecter(blazeConvertibleErrorCode, blazeError.localizedDescription, error)
        }
    }

}

public func handleError(
    _ rejecter: @escaping RCTPromiseRejectBlock,
    errMessage: String
) {
    let error = NSError(
        domain: blazeReactNativeErrorCode,
        code: 0,
        userInfo: [NSLocalizedDescriptionKey: errMessage])
    rejecter(blazeReactNativeErrorCode, errMessage, error)
}

public struct BlazeReactNativeError: Codable {
    let domain: String?
    let reason: String?
    let message: String?
    let metadata: [String: String]?
    let underlyingError: String?
}

extension BlazeReactNativeError {
    public static func fromBlazeError(_ error: BlazeError) -> BlazeReactNativeError {
        let underlyingErrorString: String?
        if let underlyingError = error.underlyingError {
            underlyingErrorString = String(describing: underlyingError)
        } else {
            underlyingErrorString = nil
        }

        return .init(
            domain: String(describing: error.domain),
            reason: nil,  // Currently the reason could not be extracted from BlazeError.
            message: error.errorMessage,
            metadata: error.metadata,
            underlyingError: underlyingErrorString)
    }

}

extension BlazeError {
    var toReactValue: String? {
        let reactNativeError = BlazeReactNativeError.fromBlazeError(self)
        return reactNativeError.asJsonString
    }
}
