// Copyright (c) 2026 EzAdmin. All rights reserved.
// Licensed under the EzAdmin Client License. See LICENSE-CLIENT.txt.

#if EZADMIN_DISCORD_SDK
using System;
using Discord.Sdk;
using UnityEngine;
using UnityEngine.Events;

namespace EzAdmin.Client
{

    [DisallowMultipleComponent]
    public sealed class EzAdminDiscord : MonoBehaviour
    {
        [Serializable]
        public class StringEvent : UnityEvent<string> { }

        public static EzAdminDiscord Instance { get; private set; }

        [EzAdminGroup("Your Discord application")]
        [Tooltip("Discord Developer Portal > your game's app > General Information > Application ID. " +
                 "The same number the server owner gave /playfab discord-app.")]
        [SerializeField] private string _applicationId = "";

        [EzAdminGroup("Events")]
        [Tooltip("A message for the player while linking, and when it finishes.")]
        [SerializeField] private StringEvent _onStatusChanged = new StringEvent();

        [SerializeField] private UnityEvent _onLinked = new UnityEvent();
        [SerializeField] private UnityEvent _onLinkFailed = new UnityEvent();

        private Client _client;
        private ulong _appId;
        private string _codeVerifier;
        private bool _linking;

        public bool IsReady => _client != null && _appId != 0;

        public bool IsLinking => _linking;

        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(this);
                return;
            }

            Instance = this;

            if (!ulong.TryParse((_applicationId ?? string.Empty).Trim(), out _appId))
            {
                Debug.LogError("EzAdminDiscord: set Application ID to your game's Discord application ID.", this);
                return;
            }

            _client = new Client();
            _client.AddLogCallback(OnLog, LoggingSeverity.Error);
            _client.SetApplicationId(_appId);
        }

        private void OnDestroy()
        {
            if (Instance == this)
            {
                Instance = null;
            }
        }

        public void LinkWithDiscord()
        {
            LinkWithDiscord(null);
        }

        public void LinkWithDiscord(Action<bool, string> onComplete)
        {
            if (_linking)
            {
                return;
            }

            var ezadmin = EzAdminClient.Instance;
            if (!IsReady || ezadmin == null || !ezadmin.CanLinkWithDiscord)
            {
                Finish(false, "Linking with Discord is not set up in this game.", onComplete);
                return;
            }

            _linking = true;
            SetStatus("Opening Discord...");

            var verifier = _client.CreateAuthorizationCodeVerifier();
            _codeVerifier = verifier.Verifier();

            var args = new AuthorizationArgs();
            args.SetClientId(_appId);

            args.SetScopes(Client.GetDefaultPresenceScopes() + " identify");
            args.SetCodeChallenge(verifier.Challenge());

            _client.Authorize(args, (result, code, redirectUri) =>
            {
                if (!result.Successful())
                {
                    Finish(false, "Discord sign-in was cancelled.", onComplete);
                    return;
                }

                SetStatus("Signing in...");
                _client.GetToken(_appId, code, _codeVerifier, redirectUri,
                    (tokenResult, accessToken, refreshToken, tokenType, expiresIn, scope) =>
                    {
                        if (!tokenResult.Successful() || string.IsNullOrEmpty(accessToken))
                        {
                            Finish(false, "Discord did not sign you in. Try again.", onComplete);
                            return;
                        }

                        SetStatus("Linking your account...");
                        ezadmin.LinkWithDiscordToken(accessToken,
                            (succeeded, message) => Finish(succeeded, message, onComplete));
                    });
            });
        }

        private void Finish(bool succeeded, string message, Action<bool, string> onComplete)
        {
            _linking = false;
            SetStatus(message);

            if (succeeded)
            {
                _onLinked.Invoke();
            }
            else
            {
                _onLinkFailed.Invoke();
            }

            if (onComplete != null)
            {
                onComplete(succeeded, message);
            }
        }

        private void SetStatus(string message)
        {
            _onStatusChanged.Invoke(message ?? string.Empty);
        }

        private void OnLog(string message, LoggingSeverity severity)
        {
            Debug.Log("EzAdminDiscord: " + message, this);
        }
    }
}
#endif
