using System;
using System.Reflection;
using System.Threading.Tasks;
using _Combo;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace _Core
{
    internal static class TapTapLogin
    {
        public static Type tapLoginType = Assembly.Load("TapSDK.Login.Runtime").GetType("TapSDK.Login.TapTapLogin");

        public static async Task LoginWithScopes(string[] scopes, Action<TapTapLoginResult> onSuccess, Action<InternalError> onError)
        {
            if (tapLoginType == null)
            {
                Log.W("Failed to get type TapLogin");
            }
            else
            {
                MethodInfo methodWithParam = tapLoginType.GetMethod
                (
                    "LoginWithScopes",
                    BindingFlags.Instance | BindingFlags.Public,
                    null,
                    new Type[] { typeof(string[]) },
                    null
                );

                if (methodWithParam != null)
                {
                    try
                    {
                        ConstructorInfo constructor = tapLoginType.GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic,
                            null,
                            Type.EmptyTypes,
                            null
                        );

                        if (constructor == null)
                        {
                            Log.W("Failed to get private constructor for TapLogin");
                            onError(new InternalError("Failed to get private constructor for TapLogin", new LoginError("taptap")));
                            return;
                        }

                        object targetInstance = constructor.Invoke(null);

                        Task result = (Task)methodWithParam.Invoke(targetInstance, new object[] { scopes });
                        await result;

                        PropertyInfo resultProperty = result.GetType().GetProperty("Result");
                        object accessToken = resultProperty.GetValue(result);
                        if (accessToken == null)
                        {
                            Log.E("LoginWithScopes result is null.");
                            onError(new InternalError("LoginWithScopes result is null.", new LoginError("taptap")));
                            return;
                        }
                        MethodInfo toJsonMethod = accessToken.GetType().GetMethod("ToJson");
                        string json = (string)toJsonMethod.Invoke(accessToken, null);

                        JObject temporaryObject = JObject.Parse(json);

                        if (temporaryObject["access_token"]?["scope"] != null)
                        {
                            string scopeString = temporaryObject["access_token"]["scope"].ToString();
                            string[] scopeArray = scopeString.Split(' ');
                            temporaryObject["access_token"]["scope"] = JArray.FromObject(scopeArray);
                        }

                        string updatedJson = temporaryObject.ToString();
                        var loginResult = JsonConvert.DeserializeObject<TapTapLoginResult>(updatedJson);

                        onSuccess?.Invoke(loginResult);
                    }
                    catch (TaskCanceledException)
                    {
                        onError(new InternalError(new UserCancelled()));
                    }
                    catch (Exception e)
                    {
                        Log.E(e);
                        onError(new InternalError(I18n.T("taptap_login_failed"), new LoginError("taptap", cause: e)));
                    }
                }
                else
                {
                    Log.W("Failed to get method Init");
                    onError(new InternalError("Failed to get method Init", new LoginError("taptap")));
                }
            }
        }

        public static void Logout()
        {
            if (tapLoginType == null)
            {
                Log.W("Failed to get type TapLogin");
            }
            else
            {
                MethodInfo logoutMethod = tapLoginType.GetMethod("Logout", BindingFlags.Instance | BindingFlags.Public);

                if (logoutMethod != null)
                {
                    ConstructorInfo constructor = tapLoginType.GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic,
                            null,
                            Type.EmptyTypes,
                            null
                        );

                    if (constructor == null)
                    {
                        Log.W("Failed to get private constructor for Logout");
                        return;
                    }

                    object targetInstance = constructor.Invoke(null);

                    logoutMethod.Invoke(targetInstance, null);
                    Log.I("Logout method invoked successfully.");
                }
                else
                {
                    Log.E("Logout method not found.");
                }
            }
        }

        public async static Task GetCurrentTapAccount(Action<TapTapLoginResult> onSuccess, Action<InternalError> onError)
        {
            if (tapLoginType == null)
            {
                Log.W("Failed to get type TapLogin");
            }
            else
            {
                try
                {
                    MethodInfo getCurrentTapAccountMethod = tapLoginType.GetMethod("GetCurrentTapAccount", BindingFlags.Instance | BindingFlags.Public);

                    if (getCurrentTapAccountMethod != null)
                    {
                        ConstructorInfo constructor = tapLoginType.GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic,
                            null,
                            Type.EmptyTypes,
                            null
                        );

                        if (constructor == null)
                        {
                            Log.W("Failed to get private constructor for TapLogin");
                            onError(new InternalError("Failed to get private constructor for TapLogin", new LoginError("taptap")));
                            return;
                        }

                        object targetInstance = constructor.Invoke(null);

                        Task result = (Task)getCurrentTapAccountMethod.Invoke(targetInstance, null);
                        await result;

                        PropertyInfo resultProperty = result.GetType().GetProperty("Result");
                        object accessToken = resultProperty.GetValue(result);
                        if (accessToken == null)
                        {
                            onSuccess?.Invoke(null);
                            return;
                        }
                        MethodInfo toJsonMethod = accessToken.GetType().GetMethod("ToJson");
                        string json = (string)toJsonMethod.Invoke(accessToken, null);

                        JObject temporaryObject = JObject.Parse(json);

                        if (temporaryObject["access_token"]?["scope"] != null)
                        {
                            string scopeString = temporaryObject["access_token"]["scope"].ToString();
                            string[] scopeArray = scopeString.Split(' ');
                            temporaryObject["access_token"]["scope"] = JArray.FromObject(scopeArray);
                        }

                        string updatedJson = temporaryObject.ToString();
                        var loginResult = JsonConvert.DeserializeObject<TapTapLoginResult>(updatedJson);

                        onSuccess?.Invoke(loginResult);
                    }
                    else
                    {
                        Log.E("GetCurrentTapAccount method not found.");
                        onError(new InternalError("GetCurrentTapAccount method not found.", new LoginError("taptap")));
                    }
                }
                catch (Exception e)
                {
                    onError(new InternalError(e.Message, new LoginError("taptap")));
                }
            }
        }
    }
}