//
//  WebViewController.swift
//  OrderScheduling
//
//  Created by 中道 on 2023/8/7.
//

import Foundation
import UIKit
import WebKit
import SnapKit
import DDLogKit_Private
import DDToastKit_Private
import DDControlsKit_Private
import DDUIKit
import SafariServices
import CoreLocation
import DDMAMapKit_Private

let WebViewPayCallbackNotification = NSNotification.Name(rawValue: "WebViewPayCallbackNotification")

class WebViewController : ZDViewController {
    lazy var webView: WKWebView = {
        let config = WKWebViewConfiguration()
        config.userContentController = WKUserContentController()
        
        // 注入环境标识
        let script = WKUserScript(source: "window.__IN_APP_WEBVIEW__ = true;", injectionTime: .atDocumentStart, forMainFrameOnly: false)
        config.userContentController.addUserScript(script)
        
        let webView = WKWebView(frame: .zero, configuration: config)
        return webView
    }()
    
    lazy var statusBarBackgroundView = DDView.init()
    var showNavBar : Bool
    /// 需要隐藏原生导航栏的子页面 URL path 列表，由 WebViewTool 按需配置
    var hiddenNavBarURLPaths: [String]?
    public var vcTitle : String?
    var url : String?
    var screenEdgePanGestureRecognizerEnable : Bool
    public var disappearHandler: (() -> Void)?
    
    private let webViewURLKeyPath = "URL"
    private let locationManager = CLLocationManager()
    private var locationCallback: String?

    public init(showNavBar:Bool = true,title:String?,url:String,screenEdgePanGestureRecognizerEnable:Bool = true, hiddenNavBarURLPaths: [String]? = nil) {
        self.showNavBar = showNavBar
        self.hiddenNavBarURLPaths = hiddenNavBarURLPaths
        self.vcTitle = title
        self.url = url
        self.screenEdgePanGestureRecognizerEnable = screenEdgePanGestureRecognizerEnable
        super.init(nibName: nil, bundle: nil)
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        webView.removeObserver(self, forKeyPath: webViewURLKeyPath)
        if disappearHandler != nil {
            disappearHandler!()
        }
    }
    
    open override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .hex("FAFAFA")
        
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.backgroundColor = .white
        webView.navigationDelegate = self
        webView.uiDelegate = self
        webView.addObserver(self, forKeyPath: webViewURLKeyPath, options: .new, context: nil)
        view.addSubview(webView)
        
        let request = URLRequest(url: URL(string: url!)!,cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
        webView.load(request)
        
        if showNavBar == false {
            navigationItem.leftBarButtonItems = nil
            dd_navigationBarBackgroundColor = .clear

            view.addSubview(statusBarBackgroundView)
        }else{
            title = vcTitle
            dd_navigationBarTitleTextAttributes = [.foregroundColor : UIColor.white(alpha: 0.7),.font:UIFont.mediumFont(17)]
            dd_navigationBarBarButtonItemAttributes = [.foregroundColor : UIColor.white]
        }
        
        view.dd_showHUD()
        
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        
        NotificationCenter.default.addObserver(self, selector: #selector(handlePayCallback(_:)), name: WebViewPayCallbackNotification, object: nil)
    }
    
    override func viewSafeAreaInsetsDidChange() {
        super.viewSafeAreaInsetsDidChange()
        updateWebViewConstraints()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updateNavBarVisibility()
        if screenEdgePanGestureRecognizerEnable == false {
            let nav = navigationController as? DDNavigationController
            nav?.screenEdgePanGestureRecognizerEnable(false)
        }
        addScriptMessageHandlers()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if showNavBar == false {
            navigationController?.navigationBar.isHidden = false
        }
        if screenEdgePanGestureRecognizerEnable == false {
            let nav = navigationController as? DDNavigationController
            nav?.screenEdgePanGestureRecognizerEnable(true)
        }
        removeScriptMessageHandlers()
    }
    
    override func dd_backActionPop(_ isAnimated: Bool) {
        if webView.canGoBack == true {
            webView.goBack()
        }else{
            super.dd_backActionPop(isAnimated)
        }
    }
    
    private func isNavBarHidden() -> Bool {
        if showNavBar == false { return true }
        guard let hiddenPaths = hiddenNavBarURLPaths, let currentUrl = webView.url else { return false }
        let currentPath = currentUrl.path
        return hiddenPaths.contains(currentPath)
    }
    
    private func updateNavBarVisibility() {
        let hidden = isNavBarHidden()
        navigationController?.navigationBar.isHidden = hidden
        if showNavBar && !hidden {
            dd_navigationBarBackgroundColor = .hex("2C395F")
        }
        // 强制触发布局更新，使 viewSafeAreaInsetsDidChange 能拿到正确的 safeAreaInsets
        view.setNeedsLayout()
        view.layoutIfNeeded()
    }
    
    private func updateWebViewConstraints() {
        let hidden = isNavBarHidden()
        let safeAreaTop = view.safeAreaInsets.top
        if showNavBar == false || hidden {
            if statusBarBackgroundView.superview == nil {
                view.addSubview(statusBarBackgroundView)
            }
            statusBarBackgroundView.backgroundColor = .hex("2C395F")
            statusBarBackgroundView.snp.remakeConstraints { make in
                make.top.left.right.equalToSuperview()
                make.height.equalTo(safeAreaTop)
            }
            
            webView.snp.remakeConstraints { make in
                make.left.right.equalToSuperview()
                make.top.equalTo(statusBarBackgroundView.snp.bottom)
                make.bottom.equalTo(-view.safeAreaInsets.bottom)
            }
        } else {
            webView.snp.remakeConstraints { make in
                make.left.right.equalToSuperview()
                make.top.equalTo(safeAreaTop)
                make.bottom.equalTo(-view.safeAreaInsets.bottom)
            }
        }
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == webViewURLKeyPath {
            updateNavBarVisibility()
        }
    }
    
    private func addScriptMessageHandlers() {
        let handlers = ["nativeObject", "scan", "getLocation", "openMapNavigation", "callPhone", "openDocument"]
        handlers.forEach { name in
            webView.configuration.userContentController.add(self, name: name)
        }
    }
    
    private func removeScriptMessageHandlers() {
        let handlers = ["nativeObject", "scan", "getLocation", "openMapNavigation", "callPhone", "openDocument"]
        handlers.forEach { name in
            webView.configuration.userContentController.removeScriptMessageHandler(forName: name)
        }
    }
    
    @objc private func handlePayCallback(_ notification: Notification) {
        guard let orderNo = notification.userInfo?["orderNo"] as? String else { return }
        let js = """
        (function() {
            if (window.onPayCallback) {
                window.onPayCallback({ orderNo: '\(orderNo)' });
                return true;
            }
            return false;
        })()
        """
        webView.evaluateJavaScript(js) { [weak self] result, _ in
            if let handled = result as? Bool, handled == true {
                return
            }
            // 兜底：刷新当前页面
            self?.webView.reload()
        }
    }
    
    private func evaluateCallback(_ callback: String?, with result: [String: Any]) {
        guard let callback = callback, callback.isEmpty == false else { return }
        guard let data = try? JSONSerialization.data(withJSONObject: result, options: []),
              let jsonString = String(data: data, encoding: .utf8) else {
            return
        }
        let js = "window['\(callback)'](\(jsonString));"
        webView.evaluateJavaScript(js, completionHandler: nil)
    }
}

extension WebViewController : WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        let dict = message.body as? [String:Any]
        let name = message.name
        
        if name == "nativeObject" {
            handleNativeObject(dict)
            return
        }
        
        switch name {
        case "scan":
            handleScan(dict)
        case "getLocation":
            handleGetLocation(dict)
        case "openMapNavigation":
            handleOpenMapNavigation(dict)
        case "callPhone":
            handleCallPhone(dict)
        case "openDocument":
            handleOpenDocument(dict)
        default:
            break
        }
    }
    
    private func handleNativeObject(_ dict: [String: Any]?) {
        let action = dict?["action"] as? String
        if action == "goBack" {
            navigationController?.popViewController(animated: true)
        }else if action == "goTraining" {
            let params = dict?["params"] as? [String:Any]
            let id = params?["id"] as? Int
            if let supplierId = USER.supplierId,let userId = USER.userId, let id {
                WEBTOOL.open(name: .docmentDetail, appending: "&supplierId=\(supplierId)&userId=\(userId)&id=\(id)")
            }
        }else if action == "orderPhoto" {
            let params = dict?["params"] as? [String:Any]
            if let userOrderId = params?["userOrderId"] as? String,let taskOrderId = params?["taskOrderId"] as? String,let orderCode = params?["orderCode"] as? String {
                let isAllowImage = params?["isAllowImage"] as? Int
                var canModify = true
                // 0可以补传，1不可以补传
                if isAllowImage == 1 {
                    canModify = false
                }
                let vc = AdditionalPhotoController(userOrderId: Int(userOrderId) ?? 0, orderCode: orderCode, taskOrderId: Int(taskOrderId) ?? 0,canModify: canModify)
                navigationController?.pushViewController(vc, animated: true)
            }
        }else if action == "goMonitoring" {
            let params = dict?["params"] as? [String:Any]
            let code = params?["code"] as? String
            let vc = VehicleMonitorHistoryController(code: code)
            navigationController?.pushViewController(vc, animated: true)
        } else if action == "copyToClipboard" {
            let paramsAny = dict?["params"]

            var output: String? = nil

            if let paramsDict = paramsAny as? [String: Any] {
                if let t = (paramsDict["text"] as? String) ?? (paramsDict["value"] as? String) {
                    let trimmed = t.trimmingCharacters(in: .whitespacesAndNewlines)
                    if trimmed.isEmpty == false { output = trimmed }
                }
                if output == nil {
                    output = dictToMultilineKV(paramsDict)
                }
            } else if let paramsStr = paramsAny as? String {
                if let multiline = jsonToMultilineKV(paramsStr) {
                    output = multiline
                } else {
                    let trimmed = paramsStr.trimmingCharacters(in: .whitespacesAndNewlines)
                    if trimmed.isEmpty == false { output = trimmed }
                }
            }

            let finalText = (output ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
            guard finalText.isEmpty == false else {
                view.dd_makeToast("复制内容为空")
                return
            }

            DispatchQueue.main.async {[weak self] in
                UIPasteboard.general.string = finalText
                self?.view.dd_makeToast("已复制")
            }
        }
    }
    
    // MARK: - scan
    private func handleScan(_ dict: [String: Any]?) {
        let callback = dict?["callback"] as? String
        let vc = ScanViewController { [weak self] code in
            if let code = code {
                self?.evaluateCallback(callback, with: ["success": true, "code": code])
            } else {
                self?.evaluateCallback(callback, with: ["success": false])
            }
        }
        navigationController?.pushViewController(vc, animated: true)
    }
    
    // MARK: - getLocation
    private func handleGetLocation(_ dict: [String: Any]?) {
        locationCallback = dict?["callback"] as? String
        let status = CLLocationManager.authorizationStatus()
        switch status {
        case .authorizedWhenInUse, .authorizedAlways:
            locationManager.requestLocation()
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        default:
            evaluateCallback(locationCallback, with: ["success": false, "error": "location_denied"])
            locationCallback = nil
        }
    }
    
    // MARK: - openMapNavigation
    private func handleOpenMapNavigation(_ dict: [String: Any]?) {
        let name = dict?["name"] as? String ?? ""
        let address = dict?["address"] as? String ?? ""
        let latitude = dict?["latitude"] as? Double ?? dict?["lat"] as? Double
        let longitude = dict?["longitude"] as? Double ?? dict?["lng"] as? Double
        
        guard let latitude = latitude, let longitude = longitude else {
            view.dd_makeToast("缺少导航坐标")
            return
        }
        
        // 优先高德地图
        let amapUrlString = "iosamap://route?dlat=\(latitude)&dlon=\(longitude)&dname=\(name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")&dev=0&t=0"
        if let amapUrl = URL(string: amapUrlString), UIApplication.shared.canOpenURL(amapUrl) {
            UIApplication.shared.open(amapUrl, options: [:], completionHandler: nil)
            return
        }
        
        // 回退 Apple Maps
        let appleUrlString = "http://maps.apple.com/?daddr=\(latitude),\(longitude)&q=\(name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
        if let appleUrl = URL(string: appleUrlString) {
            UIApplication.shared.open(appleUrl, options: [:], completionHandler: nil)
        }
    }
    
    // MARK: - callPhone
    private func handleCallPhone(_ dict: [String: Any]?) {
        let callback = dict?["callback"] as? String
        guard let phone = dict?["phone"] as? String, phone.isEmpty == false else {
            evaluateCallback(callback, with: ["success": false, "error": "phone_empty"])
            return
        }
        let tel = phone.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "-", with: "")
        guard let url = URL(string: "tel://\(tel)") else {
            evaluateCallback(callback, with: ["success": false, "error": "phone_invalid"])
            return
        }
        UIApplication.shared.open(url, options: [:]) { [weak self] success in
            self?.evaluateCallback(callback, with: ["success": success])
        }
    }
    
    // MARK: - openDocument
    private func handleOpenDocument(_ dict: [String: Any]?) {
        guard let urlString = dict?["url"] as? String, let url = URL(string: urlString) else {
            view.dd_makeToast("文档地址无效")
            return
        }
        let vc = SFSafariViewController(url: url)
        vc.modalPresentationStyle = .fullScreen
        present(vc, animated: true)
    }
}

extension WebViewController : CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        let result: [String: Any] = [
            "success": true,
            "latitude": location.coordinate.latitude,
            "lat": location.coordinate.latitude,
            "y": location.coordinate.latitude,
            "longitude": location.coordinate.longitude,
            "lng": location.coordinate.longitude,
            "x": location.coordinate.longitude,
            "lon": location.coordinate.longitude,
            "coordType": "wgs84",
            "accuracy": location.horizontalAccuracy
        ]
        evaluateCallback(locationCallback, with: result)
        locationCallback = nil
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        evaluateCallback(locationCallback, with: ["success": false, "error": error.localizedDescription])
        locationCallback = nil
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse || status == .authorizedAlways {
            if locationCallback != nil {
                locationManager.requestLocation()
            }
        } else if status != .notDetermined, locationCallback != nil {
            evaluateCallback(locationCallback, with: ["success": false, "error": "location_denied"])
            locationCallback = nil
        }
    }
}

private func unwrapEscapedJSONString(_ raw: String) -> String {
    let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
    if trimmed.hasPrefix("\""), trimmed.hasSuffix("\""), trimmed.count >= 2 {
        return String(trimmed.dropFirst().dropLast())
            .replacingOccurrences(of: "\\\"", with: "\"")
            .replacingOccurrences(of: "\\\\", with: "\\")
    }
    return trimmed
}

func jsonToMultilineKV(_ json: String) -> String? {
    let s = unwrapEscapedJSONString(json)
    guard let data = s.data(using: .utf8) else { return nil }

    guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil }

    guard let dict = obj as? [String: Any] else { return nil }
    return dictToMultilineKV(dict)
}

func dictToMultilineKV(_ dict: [String: Any]) -> String {

    let lines = dict
        .map { (k, v) -> String in
            let key = k.trimmingCharacters(in: .whitespacesAndNewlines)
                .replacingOccurrences(of: "：", with: ":")
            let keyOut = key.hasSuffix(":") ? key : (key + ":")
            return "\(keyOut)\(String(describing: v))"
        }
        .sorted()

    return lines.joined(separator: "\n")
}

extension WebViewController : WKNavigationDelegate,WKUIDelegate {
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        view.dd_hideHUD()
        DDLog(message: "finish--------------------------------\(String(describing: webView.url?.absoluteString))")
        updateNavBarVisibility()
        if showNavBar == false {
            statusBarBackgroundView.backgroundColor = .hex("2C395F")
        }
    }
    public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        view.dd_hideHUD()
        DDLog(message: "didFail--------------------------------\(String(describing: webView.url?.absoluteString))")
        view.dd_makeToast(webViewLoadFailed) {[weak self] _ in
            self?.navigationController?.popViewController(animated: true)
        }
    }
    public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        view.dd_hideHUD()
        DDLog(message: "didFailProvisionalNavigation--------------------------------\(String(describing: webView.url?.absoluteString))")
    }
    public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) {
        guard let url = navigationAction.request.url else {
            decisionHandler(.allow)
            return
        }
        let urlString = url.absoluteString
        let scheme = url.scheme?.lowercased() ?? ""
        
        // tel: 拨号
        if scheme == "tel", let tel = urlString.components(separatedBy: "tel:").last {
            UIApplication.shared.dd_openUrl(type: .phone, appending: tel)
            decisionHandler(.cancel)
            return
        }
        
        // 微信 / 支付宝 / 高德等外部 App Scheme，交给系统打开
        let externalSchemes = ["weixin", "wechat", "alipay", "alipays", "iosamap", "baidumap", "qqmap"]
        if externalSchemes.contains(scheme) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
            decisionHandler(.cancel)
            return
        }
        
        // 允许微信支付 H5 页面直接跳转
        if scheme == "https", let host = url.host {
            let wxHosts = ["wx.tenpay.com", "payapp.wechatpay.com", "mclient.alipay.com", "mobile.alipay.com"]
            if wxHosts.contains(host) {
                decisionHandler(.allow)
                return
            }
        }
        
        decisionHandler(.allow)
    }
}
