//
//  H5BaseViewController.swift
//  E-Wow
//
//  Created by dong on 2021/1/5.
//

// import Alamofire
// import CryptoSwift
import UIKit
import URLNavigator
import WebKit

class H5BaseViewController: CLBaseViewController {
    public var navTitleShow: Bool = true

    var targetUrl: URL! = URL(string: "")
    var lastProgress: Double! = 0

    private var observeRegister: Bool = false

    private let schemes = ["route", "closeWebview", "getUserInfo", "setLoading", "request", "modal", "share", "init", "openBrowser", "getAppVersion", "sendTrack"]

    lazy var webView: WKWebView! = {
        let config = WKWebViewConfiguration()
        let preferences = WKPreferences()
        preferences.javaScriptCanOpenWindowsAutomatically = true
        config.preferences = preferences
        config.allowsInlineMediaPlayback = true
        config.mediaTypesRequiringUserActionForPlayback = .video

        let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 320, height: 480), configuration: config)
        // 以下3行修改webView颜色是有效的✅
        webView.backgroundColor = .c.cbd
        webView.scrollView.backgroundColor = .c.cbd
        webView.isOpaque = false
        let jsFilePath = Bundle.main.path(forResource: "webview", ofType: "js")
        if let jshtml = try? String(contentsOfFile: jsFilePath!, encoding: .utf8) {
            let script = WKUserScript(source: jshtml, injectionTime: .atDocumentStart, forMainFrameOnly: true)
            webView.configuration.userContentController.addUserScript(script)
        }

        return webView
    }()

    lazy var progressView: UIProgressView = {
        let progressView = UIProgressView(frame: .zero)
        progressView.progress = 0
        progressView.setProgress(0, animated: false)
        navigationView.addSubview(progressView)
        progressView.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview()
            make.height.equalTo(1)
        }
        progressView.progressTintColor = .c.cpn//.purple
        progressView.trackTintColor = .clear
        return progressView
    }()

    lazy var closeButton: UIButton = {
        let button = UIButton(type: .custom)
        button.setImage(R.image.icon_close_20(), for: .normal)
        button.addTarget(self, action: #selector(tapNaviCloseBtn), for: .touchUpInside)
        button.isHidden = true
        navigationView.leftStackH.addArrangedSubview(button)
        button.snp.makeConstraints { make in
            make.size.equalTo(CGSize(width: 44, height: 44))
        }
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

//        view.backgroundColor = .white
//        webView.backgroundColor = .white
        webView.backgroundColor = .c.cbd
        webView.navigationDelegate = self

        view.addSubview(webView)
        webView.snp.makeConstraints { make in
            make.bottom.right.left.equalTo(self.view)
            make.top.equalTo(navigationView.snp.bottom)
        }

        navigationView.backButton.removeTarget(navigationView, action: .none, for: .touchUpInside)
        navigationView.backButton.addTarget(self, action: #selector(tapNaviBackBtn), for: .touchUpInside)

        setupBaseEvents()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        for (_, per) in schemes.enumerated() {
            webView.configuration.userContentController.add(self, name: per)
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        for (_, per) in schemes.enumerated() {
            webView.configuration.userContentController.removeScriptMessageHandler(forName: per)
        }
    }

    public func loadURL(url: URL) {
        /*
         if let lan = Languages.preferedLans.first {
             let queryItem = URLQueryItem(name: "lang", value: lan.rawValue)
             if var compoments = URLComponents(url: url, resolvingAgainstBaseURL: true){
                 if let items = compoments.queryItems, items.count > 0{
                     compoments.queryItems?.append(queryItem)
                 }else{
                     compoments.queryItems = [queryItem]
                 }
                 let afterUrl = compoments.url
                 targetUrl = afterUrl
             }
             dlog("h5 path with language: \(String(describing: targetUrl))")
         } else {
             targetUrl = url
         }
          */
        targetUrl = url
    }

    func setupBaseEvents() {
        weak var wself = self
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
            guard let url = self.targetUrl else {
                assert(false)
                return
            }

            let request = NSMutableURLRequest(url: url)
            wself?.webView.load(request as URLRequest)
        }

        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
        observeRegister = true
        progressView.setProgress(0, animated: false)
    }

    deinit {
        webView.configuration.userContentController.removeAllUserScripts()
        if observeRegister {
            webView.removeObserver(self, forKeyPath: "title")
            webView.removeObserver(self, forKeyPath: "estimatedProgress")
        }
    }

    func handleInit(msg: JSSDKMessage) {
        // wait to override the func
    }
}

extension H5BaseViewController {
    @objc func tapNaviBackBtn() {
        if webView.canGoBack {
            webView.goBack()
        } else {
            close()
        }
    }

    @objc func tapNaviCloseBtn() {
        close()
    }
}

// MARK: - Helper

extension H5BaseViewController {
    func stopHtmlVoice() {
        // ...
        let jsaudio = "var vids = document.getElementsByTagName('audio'); for( var i = 0; i < vids.length; i++ ){vids.item(i).pause()}"
        webView.evaluateJavaScript(jsaudio, completionHandler: nil)
    }

    static func clearCache() {
//        let dataStore = WKWebsiteDataStore.default()
//        dataStore.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), completionHandler: { records in
//            for record in records {
//                WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {
//                    print("♻️✅ Webview cache clear successfully\(record)")
//                })
//            }
//        })
        
        let websiteDataTypes: Set<String> = [
            WKWebsiteDataTypeDiskCache,
            WKWebsiteDataTypeMemoryCache,
            WKWebsiteDataTypeLocalStorage,
            WKWebsiteDataTypeWebSQLDatabases,
            WKWebsiteDataTypeIndexedDBDatabases
        ]

        // 从 1970 开始，意味着清除所有历史数据
        let dateFrom = Date(timeIntervalSince1970: 0)

        WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: dateFrom) {
            print("WebView data cleared")
        }
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "title" {
            if navTitleShow {
                navigationView.titleLabel.text = webView.title
            }
        } else if keyPath == "estimatedProgress" {
            updateProgress(progress: webView.estimatedProgress)
        } else {
        }
    }

    func updateProgress(progress: Double) {
        progressView.alpha = 1
        // dlog("progress : \(progress)")
        if progress > lastProgress {
            progressView.setProgress(Float(progress), animated: true)
        } else {
            progressView.setProgress(Float(progress), animated: false)
        }

        lastProgress = progress

        if progress >= 1 {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
                self?.progressView.alpha = 0
                self?.progressView.setProgress(0, animated: false)
                self?.lastProgress = 0
            }
        }
    }
}

// MARK: - Handle Event

extension H5BaseViewController {
    func handleRoute(msg: JSSDKMessage) {
        if let uri = msg.uri?.urlValue {
            navigator.open(uri)
        }
    }

    func handleRequest(msg: JSSDKMessage) {
//        let requestUri = msg.uri
//        let params = msg.params
        // for h5自主调用，静默请求...成功后执行成功回调，失败后执行error方法。

        guard let uri = msg.uri else {
            return
        }

        var requestAny: AnyCodable = AnyCodable(value: [:])

        let token = UserCore.shared.token
        if !token.isEmpty {
            do {
//                let body = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
//                let str = String(data: body, encoding: .utf8)
                var str = ""
                if let dict = msg.params, let data2: Data = try? JSONSerialization.data(withJSONObject: dict, options: []) {
                    let objcAnyCodable = try! JSONDecoder().decode(AnyCodable.self, from: data2)
                    let backToJson = try! JSONEncoder().encode(objcAnyCodable)
                    let jsonString = String(bytes: backToJson, encoding: .utf8)!
                    str = jsonString
                }

//                let key = (token + "AHkt5aUUtO6HZPid").md5().uppercased()
//                let aes = try AES(key: key, iv: "HBB4UO5kEmM4169Z")
//                let encrypted = try aes.encrypt(str.bytes)
//                let result = encrypted.toBase64()
//                let dic = ["key": result]
//                dlog("⚠️ 加密前参数：\(str)  \n⚠️ 加密结果：\(dic)")
//                if let data3: Data = try? JSONSerialization.data(withJSONObject: dic, options: []) {
//                    let objcAnyCodable = try! JSONDecoder().decode(AnyCodable.self, from: data3)
//                    requestAny = objcAnyCodable
//                }
            } catch {
            }
        } else {
            if let dict = msg.params, let data2: Data = try? JSONSerialization.data(withJSONObject: dict, options: []), let objcAnyCodable = try? JSONDecoder().decode(AnyCodable.self, from: data2) {
                requestAny = objcAnyCodable
            }
        }

//        let headers = HTTPHeaders(APIConfig.apiHeaders()!)

        Hud.showIndicator()
        dlog("☁️h5 request:\(uri) params: \(requestAny)")
//        AF.request(uri, method: .post, parameters: requestAny, encoder: JSONParameterEncoder.default, headers: headers, interceptor: nil, requestModifier: nil).responseString { [weak self] response in
//            // dlog("response: \(response)")
//            self?.view.hideToastActivity()
//            switch response.result {
//            case let .success(model):
//                guard let ltResponse: ResponseData = ResponseData<Dictionary<String, Any>>.deserialize(from: model) else {
//                    return
//                }
//
//                if ltResponse.status == "OK" {
//                    if let ltArrayResponse = ResponseData<Array<Dictionary<String, Any>>>.deserialize(from: model), let jsonDict = ltArrayResponse.content {
//                        if let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted), let str = String(data: data, encoding: .utf8) {
//                            let jsonString = str // content2.toJSONString() ?? ""
//                            let js = "\(msg.success ?? "")((\(jsonString)))"
//                            dlog("✅ success call js:\(js)")
//                            self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
//                                if error != nil {
//                                    dlog("❌ exec js error: \(error?.localizedDescription ?? "")")
//                                }
//                            })
//                            return
//                        }
//
//                    } else if let content = ltResponse.content {
//                        let jsonString = content.toJSONString()
//                        let js = "\(msg.success ?? "")((\(jsonString ?? "")))"
//                        dlog("✅ success call js:\(js)")
//                        self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
//                            if error != nil {
//                                dlog("❌ exec js error: \(error?.localizedDescription ?? "")")
//                            }
//                        })
//                        return
//                    }
//
//                    let js = "\(msg.success ?? "")()"
//                    dlog("✅ success call js no content:\(js)")
//                    self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
//                        if error != nil {
//                            dlog("❌ exec js error: \(error?.localizedDescription ?? "")")
//                        }
//                    })
//                } else {
//                    // --- 接口错误
//                    let js = "\(msg.error ?? "")((\(model)))"
//                    dlog("❌ api error: \(js)")
//                    self?.webView.evaluateJavaScript(js, completionHandler: nil)
//                }
//
//                break
//            default:
//                // --- 网络等错误
//                UIWindow.key?.makeToast(R.string.localizable.internet_connect_failed.localized())
//                let js = "\(msg.error ?? "")()"
//                dlog("❌ api network error: \(js)")
//                self?.webView.evaluateJavaScript(js, completionHandler: nil)
//                break
//            }
//        }
    }

    func handleLoading(msg: JSSDKMessage) {
        if msg.status {
            UIWindow.key?.makeToastActivity()
            
        } else {
            UIWindow.key?.hideToastActivity()
        }
    }

    func handleGetUserInfo(msg: JSSDKMessage) {
    }

    func handleModal(msg: JSSDKMessage) {
        // 取决于msg.type. 暂无
    }

    func handleOpenBrowser(msg: JSSDKMessage) {
        if let uri = msg.uri, uri.count > 0, let url = URL(string: uri) {
            UIApplication.shared.open(url, options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly: false], completionHandler: nil)
        }
    }

    func handleGetAppversion(msg: JSSDKMessage) {
        let version = Bundle.appVersion
        let dict = ["version": version]
        let data1 = try? JSONSerialization.data(withJSONObject: dict, options: [])
        let dictJs = String(data: data1!, encoding: .utf8) ?? ""
        // dict.toJSONString() ?? ""
        let js = "\(msg.success ?? "")((\(dictJs)))"
        dlog("✅ success call js:\(js)")
        webView.evaluateJavaScript(js, completionHandler: { _, error in
            if error != nil {
                dlog("❌ exec js error: \(error?.localizedDescription ?? "")")
            }
        })
    }

    private func handleDealSendTrack(msg: JSSDKMessage) {
        if let trackName = msg.name {
            var params = msg.params ?? [:]
            if let uid = UserCore.shared.user?.userId {
                // params.updateValue(uid, forKey: "userId")
            }
            // AppAnalytics.commonRecord(trackName, parameters: params)
        }
    }
}

// MARK: - 🔥 WKScriptMessageHandler

extension H5BaseViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        dlog("☁️☁️ h5 message name: \(message.name) message body: \(message.body)")
        let msgName = message.name
        guard let body = message.body as? String else {
            // elog("h5 message failed! : \(msgName)")
            return
        }

        guard let msg = CodableHelper.decode(JSSDKMessage.self, from: body) else {
            return
        }

        if msgName == "route" {
            msg.msgName = msgName
            handleRoute(msg: msg)
        } else if msgName == "request" {
            msg.msgName = msgName
            handleRequest(msg: msg)

        } else if msgName == "setLoading" {
            msg.msgName = msgName
            handleLoading(msg: msg)

        } else if msgName == "closeWebview" {
            close()
        } else if msgName == "getUserInfo" {
            msg.msgName = msgName
            handleGetUserInfo(msg: msg)

        } else if msgName == "modal" {
            msg.msgName = msgName
            handleModal(msg: msg)

        } else if msgName == "share" {
            // to do.
        } else if msgName == "init" {
            handleInit(msg: msg)

        } else if msgName == "openBrowser" {
            msg.msgName = msgName
            handleOpenBrowser(msg: msg)

        } else if msgName == "getAppVersion" {
            msg.msgName = msgName
            handleGetAppversion(msg: msg)

        } else if msgName == "sendTrack" {
            msg.msgName = msgName
            handleDealSendTrack(msg: msg)

        } else {
            // Please upgrade to the latest version
            dlog("🛎Please upgrade to the latest version,Unsupported protocol.")
        }
    }
}

// MARK: - WKNavigationDelegate`

extension H5BaseViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        guard let urlRequest = navigationAction.request.url?.absoluteString.removingPercentEncoding else {
            decisionHandler(.cancel)
            return
        }
        dlog("☁️ webview load： \(urlRequest) ☁️")
        if urlRequest.hasPrefix(AppConst.schemePrefix) {
            navigator.open(urlRequest)

        } else if urlRequest.hasPrefix("mailto:") { // open system to send email.
            if let url = URL(string: urlRequest) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            }
        }

        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        dlog("didFinish 🔥🔥🔥\(String(describing: webView.url))")

        closeButton.isHidden = !webView.canGoBack
    }
}
