#if UNITY_STANDALONE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using _Core;
using Newtonsoft.Json;
using Seayoo.ComboSDK.Windows;
using UnityEngine;
using UnityEngine.UI;
using Vuplex.WebView;

namespace _Combo
{
    [ViewPrefab("Prefabs/ComboWebView")]
    internal class WebView : View<WebView>
    {
        public string url = "";
        public int width = 80;
        public int height = 80;
        public CanvasRenderer navigationBar;
        public RectTransform comboWebviewRect;
        public LayoutElement contentLayout;
        public Text reloadText;
        public Button closeBtn;
        public Button backBtn;
        public Button forwardBtn;
        public Button refreshBtn;
        public Button reloadBtn;
        public RectTransform webviewRect;
        public CanvasRenderer errorPanel;
        public bool showNavigationBar;
        public Image background;
        public HorizontalLayoutGroup topHorizontal;
        public LayoutElement webviewLayoutElement;
        private CanvasWebViewPrefab canvasWebViewPrefab;
        private Action onCloaseAction;
        private bool isSuccess = false;
        private bool backgroundEnable;
        private WebViewType webViewType = WebViewType.General;
        private DragMode dragMode = DragMode.DragWithinPage;
        private static readonly Dictionary<string, JSBridgeCmd> _cmdMapping = new Dictionary<string, JSBridgeCmd>
        {
            { "closeEvent", JSBridgeCmd.CLOSEEVENT },
            { "close" , JSBridgeCmd.CLOSE },
            { "open_in_browser", JSBridgeCmd.OPEN_IN_BROWSER },
            { "open_url", JSBridgeCmd.OPEN_URL },
            { "list", JSBridgeCmd.LIST },
            { "open_short_link", JSBridgeCmd.OPEN_SHORT_LINK },
            { "captcha_result", JSBridgeCmd.CAPTCHA_RESULT },
            { "game_assets", JSBridgeCmd.GAME_ASSETS }
        };

        async void Start()
        {
            EventSystem.Register(this);
            UIController.Instance.ShowLoading(0.5f);

            if(width == 0 || height == 0)
            {
                width = 80;
                height = 80;
            }
            if(width < 0 || width > 100)
            {
                width = 80;
            }
            if(height < 0 || height > 100)
            {
                height = 80;
            }
            webviewRect.localScale = new Vector2((float)width/100, (float)height/100);
            canvasWebViewPrefab = CanvasWebViewPrefab.Instantiate();
            canvasWebViewPrefab.Visible = false;
            canvasWebViewPrefab.transform.SetParent(webviewRect, false);
            canvasWebViewPrefab.DragMode = dragMode;

            await canvasWebViewPrefab.WaitUntilInitialized();

            canvasWebViewPrefab.WebView.SetDefaultBackgroundEnabled(!backgroundEnable);
            canvasWebViewPrefab.WebView.MessageEmitted += MessageEmitted;
            canvasWebViewPrefab.WebView.LoadProgressChanged += ProgressChanged;
            canvasWebViewPrefab.WebView.LoadUrl(url);

            var keyboardListener = FindObjectOfType<Vuplex.WebView.Internal.NativeKeyboardListener>();
            if (keyboardListener != null) {
                // 启用组件
                keyboardListener.enabled = true;
            }
            Input.imeCompositionMode = IMECompositionMode.On;
        }

        void Awake()
        {
            closeBtn.onClick.AddListener(()=>{
                CloseIME();
                onCloaseAction?.Invoke();
                Destroy();
            });
            backBtn.onClick.AddListener(() => canvasWebViewPrefab.WebView.GoBack());
            forwardBtn.onClick.AddListener(() => canvasWebViewPrefab.WebView.GoForward());
            refreshBtn.onClick.AddListener(() => canvasWebViewPrefab.WebView.Reload());
            reloadBtn.onClick.AddListener(() => canvasWebViewPrefab.WebView.Reload());
            backBtn.interactable = false;
            forwardBtn.interactable = false;
            reloadText.text = I18n.T("webview_reload_button");
        }

        void OnDestroy()
        {
            EventSystem.UnRegister(this);
            closeBtn.onClick.RemoveListener(()=>{
                CloseIME();
                onCloaseAction?.Invoke();
                Destroy();
            });
            backBtn.onClick.RemoveListener(() => canvasWebViewPrefab.WebView.GoBack());
            forwardBtn.onClick.RemoveListener(() => canvasWebViewPrefab.WebView.GoForward());
            refreshBtn.onClick.RemoveListener(() => canvasWebViewPrefab.WebView.Reload());
            reloadBtn.onClick.RemoveListener(() => canvasWebViewPrefab.WebView.Reload());
            canvasWebViewPrefab.WebView.LoadProgressChanged -= ProgressChanged;
            canvasWebViewPrefab.WebView.MessageEmitted -= MessageEmitted;
        }

        async void ProgressChanged (object sender, ProgressChangedEventArgs eventArgs)
        {
            if(eventArgs.Type == ProgressChangeType.Failed || eventArgs.Type == ProgressChangeType.Finished)
            {
                UIController.Instance.HideLoading();
                canvasWebViewPrefab.Visible = true;
                if (showNavigationBar)
                {
                    navigationBar.gameObject.SetActive(true);
                    float h = comboWebviewRect.rect.height;
                    contentLayout.minHeight = (float)(h * 0.95);
                    background.color = new Color(1, 1, 1, 1);
                    topHorizontal.spacing = (Screen.width - 250) / 2;
                    webviewLayoutElement.enabled = true;
                    webviewLayoutElement.minHeight = Screen.height - 50;
                }
            }
            if(eventArgs.Type == ProgressChangeType.Failed)
            {
                errorPanel.gameObject.SetActive(true);
                LoadFailed.Invoke(new LoadFailed{ webViewType = webViewType});
                if(webViewType != WebViewType.General)
                {
                    Destroy();
                }
            }
            if(eventArgs.Type == ProgressChangeType.Finished || eventArgs.Type == ProgressChangeType.Updated)
            {
                errorPanel.gameObject.SetActive(false);
            }
            var canGoBack = await canvasWebViewPrefab.WebView.CanGoBack();
            ChangeButtonInteractable(canGoBack, backBtn);
            var canGoForward = await canvasWebViewPrefab.WebView.CanGoForward();
            ChangeButtonInteractable(canGoForward, forwardBtn);
        }
        
        public void MessageEmitted(object sender, EventArgs<string> eventArgs)
        {
            try
            {
                if(eventArgs == null || string.IsNullOrEmpty(eventArgs.Value))
                {
                    return;
                }

                // 为了兼容老版本 JS桥 closeEvent
                if(eventArgs.Value == "closeEvent")
                {
                    CloseView();
                    return;
                }

                JSBridgeCmd cmd;
                Command command = JsonConvert.DeserializeObject<Command>(eventArgs.Value);
                _cmdMapping.TryGetValue(command.cmd, out cmd);
                if(cmd == JSBridgeCmd.CLOSE)
                {
                    CloseView();
                }
                else if(cmd == JSBridgeCmd.OPEN_IN_BROWSER)
                {
                    OpenInBrowser(command);
                }
                else if(cmd == JSBridgeCmd.OPEN_URL)
                {
                    OpenUrl(command);
                }
                else if(cmd ==JSBridgeCmd.LIST)
                {
                    List();
                }
                else if(cmd == JSBridgeCmd.OPEN_SHORT_LINK)
                {
                    OpenShortLink(command);
                }
                else if(cmd == JSBridgeCmd.CAPTCHA_RESULT)
                {
                    CaptchaResult(command);
                }
                else if(cmd == JSBridgeCmd.GAME_ASSETS)
                {
                    GameAssets(command);
                }
            }
            catch(Exception ex)
            {
                Log.E($"Invalid JSON message: {ex.Message}");
                var error = new InternalError(ex);
                ErrorTrackingManager.Instance.CaptureException(error);
            }
        }

        public void SetUrl(string url) {
            this.url = url;
        }

        public void SetShowNavigationBar(bool showNavigationBar)
        {
            this.showNavigationBar = showNavigationBar;
        }

        public void SetCloseAction(Action action)
        {
            onCloaseAction = action;
        }

        public void SetWidthAndHeight(int width, int height)
        {
            this.width = width;
            this.height = height;
        }

        public void SetType(WebViewType webViewType)
        {
            this.webViewType = webViewType;
        }

        public void SetDefaultBackgroundEnabled(bool enable)
        {
            backgroundEnable = enable;
        }

        public void SetDragMode(DragMode dragMode)
        {
            this.dragMode = dragMode;
        }

        protected override IEnumerator OnShow()
        {
            yield return null;
        }
        protected override IEnumerator OnHide()
        {
            yield return null;
        }

        private void ChangeButtonInteractable(bool interactable, Button button)
        {
            button.interactable = interactable;
        }

        private void CloseIME()
        {
            var keyboardListener = FindObjectOfType<Vuplex.WebView.Internal.NativeKeyboardListener>();
            if (keyboardListener != null) {
                // 禁用组件
                keyboardListener.enabled = false;
            }
            Input.imeCompositionMode = IMECompositionMode.Off;
            UnityEngine.EventSystems.EventSystem.current.SetSelectedGameObject(null);
        }

        private void CloseView()
        {
            if(isSuccess)
            {
                return;
            }
            isSuccess = true;
            CloseIME();
            LoadSuccess.Invoke(new LoadSuccess { webViewType = webViewType});
            Destroy();
        }

        [Obsolete("1.22.0 版本后建议使用 openUrl")]
        private void OpenInBrowser(Command command)
        {
            var args = (OpenInBrowserArgs)command.args;
            Application.OpenURL(args.url);
        }

        private void OpenUrl(Command command)
        {
            var args = (OpenInBrowserArgs)command.args;
            if(string.IsNullOrEmpty(args.url))
            {
                Log.I("Failed to open link: url is empty");
                return;
            }
            Application.OpenURL(args.url);
        }

        private void List()
        {
            var obj = new JSCallback
            {
                cmd = "list",
                response = new ListJSResponse
                {
                    supportedCmds = _cmdMapping.Keys.ToArray()
                }
            };
            var jsCallback = $"window.handleComboCallback('{JsonConvert.SerializeObject(obj)}')";
            canvasWebViewPrefab.WebView.ExecuteJavaScript(jsCallback, null);
        }

        private void OpenShortLink(Command command)
        {
            var args = (OpenShortLinkArgs)command.args;
            var obj = new JSCallback();
            GamerManager.Instance.OpenShortLink(args.shortLink, args.gameData, result => {
                if(result.IsSuccess)
                {
                    obj = new JSCallback
                    {
                        cmd = "open_short_link",
                        response = new OpenShortLinkJSSuccessResponse
                        {
                            error = ""
                        }
                    };
                }
                else
                {
                    var error = ComboSDKError.Create(result.Error);
                    obj = new JSCallback
                    {
                        cmd = "open_short_link",
                        response = new OpenShortLinkJSFailResponse
                        {
                            error = error.Error ?? "",
                            UserMessage = error.Message ?? "",
                            // 转为 url 编码，方便 web 解析
                            DeveloperMessage = HttpUtils.UrlEncode(error.DetailMessage) ?? ""
                        }
                    };
                }
                
                var jsCallback = $"window.handleComboCallback('{JsonConvert.SerializeObject(obj)}')";
                canvasWebViewPrefab.WebView.ExecuteJavaScript(jsCallback, null);
            });
        }

        private void CaptchaResult(Command command)
        {
            var args = (CaptchaResultArgs)command.args;
            ReceiveCaptchaResult.Invoke(new ReceiveCaptchaResult
            {
                webViewType = webViewType,
                captchaId = args.captchaId,
                captchaResult = args.captchaResult
            });
            CloseIME();
            Destroy();
        }

        private void GameAssets(Command command)
        {
            var obj = new JSCallback();
            if(GlobalParameters.Instance.SdkLocalhostHttpServerEnabled && HttpServer.Instance.IsServerStart())
            {
                obj = new JSCallback
                {
                    cmd = "game_assets",
                    response = new GameAssetsSuccessResponse
                    {
                        available = true,
                        baseUrl = HttpServer.Instance.GetLocalPath(),
                        assets = HttpServer.Instance.GetComboSDKAssetsList()
                    }
                };
            }
            else
            {
                obj = new JSCallback
                {
                    cmd = "game_assets",
                    response = new GameAssetsFailResponse
                    {
                        available = false,
                    }
                };
            }
            var jsonString = JsonConvert.SerializeObject(obj);
            Debug.Log("Converted JSON string: " + jsonString);
            var jsCallback = $"window.handleComboCallback('{jsonString}')";
            canvasWebViewPrefab.WebView.ExecuteJavaScript(jsCallback, null);
        }
    }

    public enum JSBridgeCmd
    {
        CLOSEEVENT,
        CLOSE,
        OPEN_IN_BROWSER,
        OPEN_URL,
        LIST,
        OPEN_SHORT_LINK,
        CAPTCHA_RESULT,
        GAME_ASSETS
    }
}
#endif