//
// Copyright 2025 Circle Internet Group, Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import CYBAVOWallet

@objc(RNWalletSDK)
class RNWalletSDK : NSObject {

    @objc func initSDK(_ configuration: NSDictionary){
        if let endPoint = configuration["endpoint"]{
            WalletSdk.shared.endPoint = endPoint as! String
        }
        if let apiCode = configuration["apiCode"]{
            WalletSdk.shared.apiCode = apiCode as! String
        }
        if let apnsSandbox = configuration["apnsSandbox"]{
            WalletSdk.shared.apnsSandbox = apnsSandbox as! Bool
        }
    }
}

extension RNWalletSDK {
    @objc
    func constantsToExport() -> [AnyHashable : Any]! {
        var constants = [AnyHashable : Any]()
        constants["sdkInfo"] = WalletSdk.shared.getSDKInfo()

        var errorCodes = [String : Any]()
        for err in ApiError.ErrorCode.allCases {
            errorCodes[err.name] = String(err.rawValue)
        }
        constants["ErrorCodes"] = errorCodes
        return constants
    }
    @objc static func requiresMainQueueSetup() -> Bool {
        return true
    }
}

extension RNWalletSDK{
    class func toDictHasNil<T>(_ any: T) -> Any {
        let mirror = Mirror(reflecting: any)
        if let style = mirror.displayStyle {
            if style == .collection {
                var array: [Any] = []
                for (_, valueMaybe) in mirror.children {
                    let value = unwrap(valueMaybe)
                    array.append(toDictHasNil(value))
                }
                return array
            } else if style == .enum {
                return (any as? RawEnum)?.anyRawValue ?? ""
            } else {
                var dict: [String: Any] = [:]
                for (labelMaybe, valueMaybe) in mirror.children {
                    guard let label = labelMaybe else { continue }
                    if case Optional<Any>.none = valueMaybe {
                        dict[label] = nil
                    }else{
                        let value = unwrap(valueMaybe)
                        dict[label] = toDictHasNil(value)
                    }
                }
                return dict
            }
        } else {
            return any
        }
    }
    class func toCustomDict(result: SignedRawTxResult) -> Any {
      var dict : [String: Any] = [:]
      for (key, info) in result.signatures {
          dict[key] = RNWalletSDK.toDict(info)
      }
      let newdict: [String: Any] = [
          "signatures": dict,
          "signedTx": result.signedTx
      ]
      return newdict
    }

    class func toCustomDict(result: GetWalletsResult) -> Any {

     let wallets = RNWalletSDK.toDict(result.wallets)
     let dict: [String: Any] = [
         "wallets": wallets,
     ]
      return dict
    }

    class func toDict<T>(_ any: T) -> Any {
        let mirror = Mirror(reflecting: any)
        if let style = mirror.displayStyle {
            if style == .collection {
                var array: [Any] = []
                for (_, valueMaybe) in mirror.children {
                    let value = unwrap(valueMaybe)
                    array.append(toDict(value))
                }
                return array
            } else if style == .enum {
                if any is WalletType{
                    return (any as? WalletType)?.rawValue ?? ""
                } else if any is Direction{
                    return (any as? Direction)?.rawValue ?? ""
                } else if any is TokenStandard {
                    return (any as? TokenStandard)?.rawValue ?? ""
                }else if any is FinancialProductKind {
                    return (any as? FinancialProductKind)?.rawValue ?? ""
                }else if any is FinancialProductListKind {
                    return (any as? FinancialProductListKind)?.rawValue ?? ""
                }else if any is FinancialHistoryListKind {
                    return (any as? FinancialHistoryListKind)?.rawValue ?? ""
                }else if any is FinancialHistoryStatus {
                    return (any as? FinancialHistoryStatus)?.rawValue ?? ""
                }else if any is FinancialBonusKind {
                    return (any as? FinancialBonusKind)?.rawValue ?? ""
                }else if any is TransactionExplainKind {
                    return (any as? TransactionExplainKind)?.rawValue ?? ""
                }else if any is TransactionType {
                    print("TransactionType \(any), \((any as? TransactionType)?.rawValue ?? -1)")
                    return (any as? TransactionType)?.rawValue ?? ""
                }
                return (any as? RawEnum)?.anyRawValue ?? ""
            } else {
                var dict: [String: Any] = [:]
                for (labelMaybe, valueMaybe) in mirror.children {
                    guard let label = labelMaybe else { continue }
                    let value = unwrap(valueMaybe)
                    dict[label] = toDict(value)
                }
                return dict
            }
        } else {
            return any
        }
    }

    class func toErrorUserInfo(_ error: CYBAVOWallet.ApiError) -> [String: Any] {
        var dict: [String: Any] = [:]
        dict["detailMessage"] = error.detailMessage
        return dict
    }

    class func unwrap<T>(_ any: T) -> Any
    {
        let mirror = Mirror(reflecting: any)
        guard mirror.displayStyle == .optional, let first = mirror.children.first else {
            return any
        }
        return first.value
    }

}

public protocol RawEnum {
    var anyRawValue: Any { get }
}

public extension RawEnum where Self: RawRepresentable {
    public var anyRawValue: Any {
        get {
            let mirror = Mirror(reflecting: self)
            if mirror.displayStyle != .enum {
                print("WARNING: You can only extend an enum with the Enum protocol")
            }
            return rawValue as Any

        }
    }
}

