using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;

namespace Unity.VisualScripting
{
    public sealed class WebView
    {
        public WebView(EditorWindow host, Rect position)
        {
            Ensure.That(nameof(host)).IsNotNull(host);

            var hostView = GetHostView(host);

            if (hostView == null)
            {
                throw new InvalidOperationException("Web view host view cannot be null during initialization.");
            }

            position = ToWindowRect(position);
            webView = ScriptableObject.CreateInstance(WebViewType);
            webView.hideFlags = HideFlags.HideAndDontSave;
            WebView_InitWebView.Invoke(webView, new[] { hostView, (int)position.x, (int)position.y, (int)position.width, (int)position.height, false });
        }

        private readonly ScriptableObject webView;

        public Rect position
        {
            set
            {
                value = ToWindowRect(value);
                WebView_SetSizeAndPosition.InvokeOptimized(webView, (int)value.x, (int)value.y, (int)value.width, (int)value.height);
            }
        }

        public EditorWindow host
        {
            set
            {
                WebView_SetHostView.InvokeOptimized(webView, GetHostView(value));
            }
        }

        public bool isDestroyed => webView == null || !webView || (bool)WebView_IntPtrIsNull.InvokeOptimized(webView);

        public bool hasFocus
        {
            set
            {
                WebView_SetFocus.InvokeOptimized(webView, value);
                WebView_SetApplicationFocus.InvokeOptimized(webView, value);
            }
        }

        public void Destroy()
        {
            UnityObject.DestroyImmediate(webView);
        }

        public void Load(string uri)
        {
            if (uri.StartsWith("file:"))
            {
                WebView_LoadFile.InvokeOptimized(webView, uri);
            }
            else
            {
                WebView_LoadUrl.InvokeOptimized(webView, uri);
            }
        }

        public void Show()
        {
            WebView_Show.InvokeOptimized(webView);
        }

        public void Hide()
        {
            WebView_Hide.InvokeOptimized(webView);
        }

        public void Forward()
        {
            WebView_Forward.InvokeOptimized(webView);
        }

        public void Back()
        {
            WebView_Back.InvokeOptimized(webView);
        }

        public void Reload()
        {
            WebView_Reload.InvokeOptimized(webView);
        }

        static WebView()
        {
            try
            {
                WebViewType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.WebView", true);

                WebView_InitWebView = WebViewType.GetMethod("InitWebView", BindingFlags.Instance | BindingFlags.Public);
                WebView_LoadUrl = WebViewType.GetMethod("LoadURL", BindingFlags.Instance | BindingFlags.Public);
                WebView_LoadFile = WebViewType.GetMethod("LoadFile", BindingFlags.Instance | BindingFlags.Public);
                WebView_SetHostView = WebViewType.GetMethod("SetHostView", BindingFlags.Instance | BindingFlags.Public);
                WebView_SetSizeAndPosition = WebViewType.GetMethod("SetSizeAndPosition", BindingFlags.Instance | BindingFlags.Public);
                WebView_SetFocus = WebViewType.GetMethod("SetFocus", BindingFlags.Instance | BindingFlags.Public);
                WebView_SetApplicationFocus = WebViewType.GetMethod("SetApplicationFocus", BindingFlags.Instance | BindingFlags.Public);
                WebView_Show = WebViewType.GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);
                WebView_Hide = WebViewType.GetMethod("Hide", BindingFlags.Instance | BindingFlags.Public);
                WebView_Back = WebViewType.GetMethod("Back", BindingFlags.Instance | BindingFlags.Public);
                WebView_Forward = WebViewType.GetMethod("Forward", BindingFlags.Instance | BindingFlags.Public);
                WebView_Reload = WebViewType.GetMethod("Reload", BindingFlags.Instance | BindingFlags.Public);
                WebView_IntPtrIsNull = WebViewType.GetMethod("IntPtrIsNull", BindingFlags.Instance | BindingFlags.NonPublic);

                if (WebView_InitWebView == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "InitWebView");
                }
                if (WebView_LoadUrl == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "LoadURL");
                }
                if (WebView_LoadFile == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "LoadFile");
                }
                if (WebView_SetHostView == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "SetHostView");
                }
                if (WebView_SetSizeAndPosition == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "SetSizeAndPosition");
                }
                if (WebView_SetFocus == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "SetFocus");
                }
                if (WebView_SetApplicationFocus == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "SetApplicationFocus");
                }
                if (WebView_Show == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "Show");
                }
                if (WebView_Hide == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "Hide");
                }
                if (WebView_Back == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "Back");
                }
                if (WebView_Forward == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "Forward");
                }
                if (WebView_Reload == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "Reload");
                }
                if (WebView_IntPtrIsNull == null)
                {
                    throw new MissingMemberException(WebViewType.FullName, "IntPtrIsNull");
                }

                EditorWindow_m_Parent = typeof(EditorWindow).GetField("m_Parent", BindingFlags.Instance | BindingFlags.NonPublic);
            }
            catch (Exception ex)
            {
                throw new UnityEditorInternalException(ex);
            }
        }

        private static readonly Type WebViewType;
        private static readonly MethodInfo WebView_InitWebView; // public extern void InitWebView(GUIView host, int x, int y, int width, int height, bool showResizeHandle);
        private static readonly MethodInfo WebView_LoadUrl; // public extern void LoadURL(string url);
        private static readonly MethodInfo WebView_LoadFile; // public extern void LoadFile(string path);
        private static readonly MethodInfo WebView_SetHostView; // public extern void SetHostView(GUIView view);
        private static readonly MethodInfo WebView_SetSizeAndPosition; // public extern void SetSizeAndPosition(int x, int y, int width, int height);
        private static readonly MethodInfo WebView_SetFocus; // public extern void SetFocus(bool value);
        private static readonly MethodInfo WebView_SetApplicationFocus; // public extern void SetApplicationFocus(bool applicationFocus);
        private static readonly MethodInfo WebView_Show; // public extern void Show();
        private static readonly MethodInfo WebView_Hide; // public extern void Hide();
        private static readonly MethodInfo WebView_Back; // public extern void Back();
        private static readonly MethodInfo WebView_Forward; // public extern void Forward();
        private static readonly MethodInfo WebView_Reload; // public extern void Reload();
        private static readonly MethodInfo WebView_IntPtrIsNull; // private extern bool IntPtrIsNull();
        private static readonly FieldInfo EditorWindow_m_Parent; // internal HostView m_Parent;

        private static object GetHostView(EditorWindow window)
        {
            if (window == null)
            {
                return null;
            }

            return EditorWindow_m_Parent.GetValue(window);
        }

        private static Rect ToWindowRect(Rect rect)
        {
            return LudiqGUIUtility.Unclip(new Rect(0, 0, rect.width, rect.height));
        }
    }
}