using System;
using System.Collections;
using UnityEngine;

namespace MJGameSDK
{
    /// <summary>
    /// Steam 回调监听：Web API 验票票据 + 微交易授权。
    /// MicroTxn / Overlay 回调在 Steam Init 后即注册，避免 InitTxn 返回前漏掉事件。
    /// </summary>
    internal class MJGameSteamCallbacks : MonoBehaviour
    {
#if STEAMWORKS_NET
        private Steamworks.Callback<Steamworks.GetTicketForWebApiResponse_t> _webApiTicketCallback;
        private Steamworks.Callback<Steamworks.MicroTxnAuthorizationResponse_t> _microTxnCallback;
        private Steamworks.Callback<Steamworks.GameOverlayActivated_t> _overlayCallback;
        private Steamworks.HAuthTicket _pendingWebApiTicketHandle = Steamworks.HAuthTicket.Invalid;
#endif
        private Action<string> _onWebApiTicket;
        private Action<MJGameResult> _onWebApiTicketError;
        private Coroutine _webApiTimeoutCoroutine;
        private bool _waitingWebApiTicket;

        private ulong _pendingOrderId;
        private Action _onAuthorized;
        private Action<MJGameResult> _onDenied;
        private Coroutine _timeoutCoroutine;
        private Coroutine _heartbeatCoroutine;
        private bool _waiting;
        private bool _steamCallbacksRegistered;

        public bool IsWaiting => _waiting;

        /// <summary>
        /// SteamAPI.Init 成功后立刻调用，常驻监听 MicroTxn / Overlay。
        /// </summary>
        public void EnsureSteamCallbacksRegistered()
        {
            if (_steamCallbacksRegistered)
                return;

#if STEAMWORKS_NET
            _microTxnCallback ??= Steamworks.Callback<Steamworks.MicroTxnAuthorizationResponse_t>.Create(OnMicroTxnAuthorization);
            _overlayCallback ??= Steamworks.Callback<Steamworks.GameOverlayActivated_t>.Create(OnGameOverlayActivated);
            _steamCallbacksRegistered = true;
            MJGameLogger.Info("Steam",
                $"Steam callbacks registered. overlayEnabled={Steamworks.SteamUtils.IsOverlayEnabled()}, " +
                $"appId={Steamworks.SteamUtils.GetAppID()}, steamId={Steamworks.SteamUser.GetSteamID()}");
#endif
        }

        /// <summary>
        /// 请求用于 AuthenticateUserTicket 的 Web API ticket（异步回调返回十六进制）。
        /// </summary>
        public void BeginRequestWebApiTicket(
            string identity,
            float timeoutSeconds,
            Action<string> onTicket,
            Action<MJGameResult> onError)
        {
            CancelWebApiTicketWait();
            EnsureSteamCallbacksRegistered();

            _onWebApiTicket = onTicket;
            _onWebApiTicketError = onError;
            _waitingWebApiTicket = true;

#if STEAMWORKS_NET
            _webApiTicketCallback ??= Steamworks.Callback<Steamworks.GetTicketForWebApiResponse_t>.Create(OnGetTicketForWebApi);

            try
            {
                _pendingWebApiTicketHandle = Steamworks.SteamUser.GetAuthTicketForWebApi(identity);
                MJGameLogger.Debug("Steam",
                    $"GetAuthTicketForWebApi handle={_pendingWebApiTicketHandle}, identity=\"{identity ?? "(null)"}\", loggedOn={Steamworks.SteamUser.BLoggedOn()}");

                if (_pendingWebApiTicketHandle == Steamworks.HAuthTicket.Invalid)
                {
                    CompleteWebApiTicketError(MJGameResult.Fail(2002,
                        "GetAuthTicketForWebApi returned Invalid (check Steam client / AppID / RunCallbacks)"));
                    return;
                }
            }
            catch (Exception e)
            {
                CompleteWebApiTicketError(MJGameResult.Fail(2002, e.Message));
                return;
            }
#else
            CompleteWebApiTicketError(MJGameResult.Fail(2003, "Steamworks.NET not available"));
            return;
#endif

            if (timeoutSeconds > 0f)
                _webApiTimeoutCoroutine = StartCoroutine(WebApiTicketTimeoutRoutine(timeoutSeconds));
        }

        public void CancelWebApiTicketWait()
        {
            if (_webApiTimeoutCoroutine != null)
            {
                StopCoroutine(_webApiTimeoutCoroutine);
                _webApiTimeoutCoroutine = null;
            }

#if STEAMWORKS_NET
            if (_pendingWebApiTicketHandle != Steamworks.HAuthTicket.Invalid)
            {
                try
                {
                    Steamworks.SteamUser.CancelAuthTicket(_pendingWebApiTicketHandle);
                }
                catch (Exception e)
                {
                    MJGameLogger.Warn("Steam", $"CancelAuthTicket failed: {e.Message}");
                }

                _pendingWebApiTicketHandle = Steamworks.HAuthTicket.Invalid;
            }
#endif

            _waitingWebApiTicket = false;
            _onWebApiTicket = null;
            _onWebApiTicketError = null;
        }

        public void BeginWaitForAuthorization(
            ulong orderId,
            float timeoutSeconds,
            Action onAuthorized,
            Action<MJGameResult> onDenied)
        {
            CancelWait();
            EnsureSteamCallbacksRegistered();

            _pendingOrderId = orderId;
            _onAuthorized = onAuthorized;
            _onDenied = onDenied;
            _waiting = true;

#if STEAMWORKS_NET
            LogOverlayDiagnostics(orderId);
#else
            MJGameLogger.Warn("Steam", "STEAMWORKS_NET not defined, cannot wait for MicroTxn overlay");
            CompleteDenied(MJGameResult.Fail(2003, "Steamworks.NET not available"));
            return;
#endif

            if (timeoutSeconds > 0f)
            {
                _timeoutCoroutine = StartCoroutine(TimeoutRoutine(timeoutSeconds));
                _heartbeatCoroutine = StartCoroutine(WaitHeartbeatRoutine(timeoutSeconds));
            }
        }

        public void CancelWait()
        {
            if (_timeoutCoroutine != null)
            {
                StopCoroutine(_timeoutCoroutine);
                _timeoutCoroutine = null;
            }

            if (_heartbeatCoroutine != null)
            {
                StopCoroutine(_heartbeatCoroutine);
                _heartbeatCoroutine = null;
            }

            _waiting = false;
            _pendingOrderId = 0;
            _onAuthorized = null;
            _onDenied = null;
        }

#if STEAMWORKS_NET
        private void LogOverlayDiagnostics(ulong orderId)
        {
            var overlayEnabled = Steamworks.SteamUtils.IsOverlayEnabled();
            var appId = Steamworks.SteamUtils.GetAppID();
            var steamId = Steamworks.SteamUser.GetSteamID();
            var loggedOn = Steamworks.SteamUser.BLoggedOn();
            var fullScreen = Screen.fullScreen;
            var fullScreenMode = Screen.fullScreenMode;

            MJGameLogger.Info("Steam",
                $"Waiting for MicroTxn authorization, orderId={orderId}, timeout pending. " +
                $"overlayEnabled={overlayEnabled}, loggedOn={loggedOn}, appId={appId}, steamId={steamId}, " +
                $"fullScreen={fullScreen}, fullScreenMode={fullScreenMode}");

            if (!overlayEnabled)
            {
                MJGameLogger.Warn("Steam",
                    "Steam Overlay is DISABLED. MicroTxn dialog will not appear. " +
                    "Enable: Steam → Settings → In-Game → Enable Steam Overlay. " +
                    "Also launch the game from Steam Library (not by double-clicking exe / Editor alone).");
            }
        }

        private void OnGameOverlayActivated(Steamworks.GameOverlayActivated_t response)
        {
            MJGameLogger.Info("Steam",
                $"GameOverlayActivated: active={response.m_bActive}, userInitiated={response.m_bUserInitiated}, " +
                $"waitingMicroTxn={_waiting}, pendingOrderId={_pendingOrderId}");
        }

        private void OnGetTicketForWebApi(Steamworks.GetTicketForWebApiResponse_t response)
        {
            if (!_waitingWebApiTicket)
                return;

            if (_pendingWebApiTicketHandle != Steamworks.HAuthTicket.Invalid &&
                response.m_hAuthTicket != _pendingWebApiTicketHandle)
                return;

            if (response.m_eResult != Steamworks.EResult.k_EResultOK || response.m_cubTicket <= 0)
            {
                CompleteWebApiTicketError(MJGameResult.Fail(2002,
                    $"GetTicketForWebApi failed: {response.m_eResult}"));
                return;
            }

            var hex = BitConverter.ToString(response.m_rgubTicket, 0, response.m_cubTicket).Replace("-", "");
            MJGameLogger.Debug("Steam", $"Web API ticket ready, length={response.m_cubTicket}");
            CompleteWebApiTicket(hex);
        }

        private void OnMicroTxnAuthorization(Steamworks.MicroTxnAuthorizationResponse_t response)
        {
            // 即使当前未在等待，也打日志，便于发现「回调早到 / orderId 不匹配」
            MJGameLogger.Info("Steam",
                $"MicroTxnAuthorization received: appId={response.m_unAppID}, orderId={response.m_ulOrderID}, " +
                $"authorized={response.m_bAuthorized}, waiting={_waiting}, pendingOrderId={_pendingOrderId}");

            if (!_waiting)
            {
                MJGameLogger.Warn("Steam",
                    "MicroTxnAuthorization arrived but SDK is not waiting (callback may have come before WaitForAuthorization).");
                return;
            }

            if (response.m_ulOrderID != _pendingOrderId)
            {
                MJGameLogger.Warn("Steam",
                    $"MicroTxnAuthorization orderId mismatch: got={response.m_ulOrderID}, expected={_pendingOrderId}");
                return;
            }

            if (response.m_bAuthorized == 1)
                CompleteAuthorized();
            else
                CompleteDenied(MJGameResult.Fail(2004, "User denied Steam purchase"));
        }
#endif

        private IEnumerator WebApiTicketTimeoutRoutine(float seconds)
        {
            yield return new WaitForSeconds(seconds);
            if (!_waitingWebApiTicket)
                yield break;

            MJGameLogger.Warn("Steam", $"GetAuthTicketForWebApi timeout after {seconds}s");
            CompleteWebApiTicketError(MJGameResult.Fail(2002, "GetAuthTicketForWebApi timeout"));
        }

        private IEnumerator TimeoutRoutine(float seconds)
        {
            yield return new WaitForSeconds(seconds);
            if (!_waiting)
                yield break;

#if STEAMWORKS_NET
            MJGameLogger.Warn("Steam",
                $"MicroTxn authorization timeout after {seconds}s, orderId={_pendingOrderId}, " +
                $"overlayEnabled={Steamworks.SteamUtils.IsOverlayEnabled()}");
#else
            MJGameLogger.Warn("Steam", $"MicroTxn authorization timeout after {seconds}s, orderId={_pendingOrderId}");
#endif
            CompleteDenied(MJGameResult.Fail(2005, "Steam purchase authorization timeout"));
        }

        private IEnumerator WaitHeartbeatRoutine(float totalSeconds)
        {
            const float interval = 30f;
            var elapsed = 0f;
            while (_waiting && elapsed < totalSeconds)
            {
                yield return new WaitForSeconds(interval);
                elapsed += interval;
                if (!_waiting)
                    yield break;

#if STEAMWORKS_NET
                MJGameLogger.Info("Steam",
                    $"Still waiting MicroTxn... elapsed={elapsed:0}s/{totalSeconds:0}s, " +
                    $"orderId={_pendingOrderId}, overlayEnabled={Steamworks.SteamUtils.IsOverlayEnabled()}");
#else
                MJGameLogger.Info("Steam",
                    $"Still waiting MicroTxn... elapsed={elapsed:0}s/{totalSeconds:0}s, orderId={_pendingOrderId}");
#endif
            }
        }

        private void CompleteWebApiTicket(string hexTicket)
        {
            if (!_waitingWebApiTicket)
                return;

            var callback = _onWebApiTicket;

            // 清等待状态，但暂不 CancelAuthTicket，避免服务端 AuthenticateUserTicket 前票据失效
            if (_webApiTimeoutCoroutine != null)
            {
                StopCoroutine(_webApiTimeoutCoroutine);
                _webApiTimeoutCoroutine = null;
            }

            _waitingWebApiTicket = false;
            _onWebApiTicket = null;
            _onWebApiTicketError = null;
            callback?.Invoke(hexTicket);
        }

        private void CompleteWebApiTicketError(MJGameResult result)
        {
            if (!_waitingWebApiTicket)
                return;

            var callback = _onWebApiTicketError;
            CancelWebApiTicketWait();
            callback?.Invoke(result);
        }

        private void CompleteAuthorized()
        {
            if (!_waiting)
                return;

            var callback = _onAuthorized;
            CancelWait();
            callback?.Invoke();
        }

        private void CompleteDenied(MJGameResult result)
        {
            if (!_waiting)
                return;

            var callback = _onDenied;
            CancelWait();
            callback?.Invoke(result);
        }

        private void OnDestroy()
        {
            CancelWebApiTicketWait();
            CancelWait();
        }
    }
}
