import Foundation
import ExpoModulesCore
import Alamofire

struct Post: Decodable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

extension URLSession {
    func fetchData<T: Decodable>(for url: URL, completion: @escaping (Result<T, Error>) -> Void) {
        self.dataTask(with: url) { (data, response, error) in
            if let error = error {
                completion(.failure(error))
            }
            
            if let data = data {
                do {
                    let object = try JSONDecoder().decode(T.self, from: data)
                    completion(.success(object))
                } catch let decoderError {
                    completion(.failure(decoderError))
                }
            }
        }.resume()
    }
}

public class NekoTestModule: Module {
  // Each module class must implement the definition function. The definition consists of components
  // that describes the module's functionality and behavior.
  // See https://docs.expo.dev/modules/module-api for more details about available components.
  public func definition() -> ModuleDefinition {
    // Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
    // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
    // The module will be accessible from `requireNativeModule('NekoTest')` in JavaScript.
    Name("NekoTest")

    // Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary.
    Constants([
      "PI": Double.pi
    ])

    // Defines event names that the module can send to JavaScript.
    Events("onChange")

    // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
    Function("hello") {
      return "Hello world! 👋"
    }

    // Defines a JavaScript function that always returns a Promise and whose native code
    // is by default dispatched on the different thread than the JavaScript runtime runs on.
    AsyncFunction("setValueAsync") { (value: String) in
      // Send an event to JavaScript.
      self.sendEvent("onChange", [
        "value": value
      ])
    }

    AsyncFunction("getPosts") { (parameters: Parameters, promise: Promise) in
        let accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3YWxsZXRfaWQiOiI2OTVjM2U1Yi1iMDBhLTQ3MTYtODY2MS00NjFkODk1M2EyNmQiLCJyb2xlIjoidXNlciIsIndhbGxldF9hZGRyZXNzIjoiRGlzWHdWbTFUNmpkYWp5S1g2Rm9NbVNKOThDekNQY1dXcVVBSjN4VUFTYzkiLCJpYXQiOjE2NjMyMzAwNTIsImV4cCI6MTY2MzgzNDg1Mn0.ALUvP4OlnBlVl8FzHa40z8slydUAKCmvaumxsoKYy5A"
        
        let headers: HTTPHeaders = [.authorization(bearerToken: accessToken)]
        
        AF.request("https://nekowallet-dev-api.nekoglobaldev.com/v2/marketplace/magic-eden/wallets/listings", parameters: parameters, encoding: URLEncoding(destination: .queryString), headers: headers).responseString() { response in
            switch(response.result) {
                case .success(let string):
                    promise.resolve(string)
                    break
                case .failure(let error):
                    promise.resolve("error")
                    break
            }
        }
    }

    // Enables the module to be used as a view manager. The view manager definition is built from
    // the definition components used in the closure passed to viewManager.
    // Definition components that are accepted as part of the view manager definition: `View`, `Prop`.
    ViewManager {
      // Defines the factory creating a native view when the module is used as a view.
      View {
        NekoTestView()
      }

      // Defines a setter for the `name` prop.
      Prop("name") { (view: NekoTestView, prop: String) in
        print(prop)
      }
    }
  }
}
