import JavaScriptCore
import Alamofire

func downloadJSFile(url: URL, completion: @escaping (Result<String, Error>) -> Void) {
    AF.download(url).responseData { (response) in
        switch response.result {
        case .success(let data):
            if let jsString = String(data: data, encoding: .utf8) {
                completion(.success(jsString))
            } else {
                completion(.failure(NSError(domain: "Download Error", code: -1, userInfo: nil)))
            }
        case .failure(let error):
            completion(.failure(error))
        }
    }
}

func createJSContext() -> JSContext {
    let ctx = JSContext()
    
    // 注入 console.log
    ctx?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
    let consoleLog: @convention(block) (String) -> Void = { message in
        print("JS 打印: \(message)")
    }
    ctx?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol)?)
    
    // 注入AF
    ctx?.evaluateScript("var AF = { request: function(url, method, data, headers, callback) { _request(url, method, data, headers, callback) } }")
    let af: @convention(block) (String, String, String?, [String: String]?, JSValue?) -> Void = { url, method, data, headers, callback in
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = method
        
        if method == "POST" {
            if let data = data {
                request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                request.httpBody = data.data(using: .utf8)
            }
        }
        
        if let headers = headers {
            request.headers = HTTPHeaders(headers)
        }
        
        AF.request(request).response { response in
            if let data = response.data {
                callback?.call(withArguments: [String(data: data, encoding: .utf8), nil])
            }
            if let error = response.error {
                debugPrint(response)
                callback?.call(withArguments: [nil, error.localizedDescription])
            }
        }
    }
    ctx?.setObject(unsafeBitCast(af, to: AnyObject.self), forKeyedSubscript: "_request" as (NSCopying & NSObjectProtocol)?)
    
    // 捕捉JS运行异常
    ctx?.exceptionHandler = { context, exception in
        if let exception = exception {
            debugPrint(exception)
            print("JS 执行异常: \(exception)")
        }
    }
    
    return ctx!
}

func testDetail(url: String, ctx: JSContext) -> Void {
    if let detailFunction = ctx.objectForKeyedSubscript("detail") {
        let result = detailFunction.call(withArguments: [url])
        let completionHandler: @convention(block) (JSValue) -> Void = { result in
            print("详情结果!!: \(result.toDictionary())")
        }
        let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self)
        result?.invokeMethod("then", withArguments: [completionFunction])
    }
}

func testSearch(keyword: String, ctx: JSContext) -> Void {
    if let search = ctx.objectForKeyedSubscript("search") {
        let result = search.call(withArguments: [keyword])
        let completionHandler: @convention(block) (JSValue) -> Void = { result in
            print("搜索结果!!: \(result.toDictionary())")
        }
        let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self)
        result?.invokeMethod("then", withArguments: [completionFunction])
    }
}

let ctx = createJSContext()

if let url = URL(string: "http://hubgit.cn/ben/be-ytb/raw/master/js/info.js") {
    downloadJSFile(url: url) { result in
        switch result {
        case .success(let jsString):
            print("下载远程JS成功")
            ctx.evaluateScript(jsString)
            testDetail(url: "https://www.youtube.com/watch?v=d0R-JyU4Btk", ctx: ctx)
//            testSearch(keyword: "周杰伦", ctx: ctx)
        case .failure(let error):
            print("Download Error: \(error)")
        }
    }
}

RunLoop.main.run()
