#nullable enable
using System;
using System.Threading;
using System.Runtime.CompilerServices;
using UnityEngine;

[assembly: InternalsVisibleTo("IGP.Multiplayer.Mirror")]

namespace IGP.Multiplayer
{
    public enum IGPMultiplayerLogLevel
    {
        /// <summary>Disables Multiplayer logging.</summary>
        Off = 0,

        /// <summary>Terminal failures that stop the requested operation or connection.</summary>
        Error = 1,

        /// <summary>Recoverable failures, degraded behavior, rejected input, or dropped data.</summary>
        Warning = 2,

        /// <summary>Low-frequency lifecycle transitions and successful connection milestones.</summary>
        Info = 3,

        /// <summary>Internal decisions, idempotent skips, queue activity, and detailed diagnostics.</summary>
        Debug = 4,
    }

    public class IGPMultiplayerException : Exception
    {
        public string? ErrorCode { get; }

        public IGPMultiplayerException(string message) : base(message) { }

        public IGPMultiplayerException(string message, string errorCode) : base(message)
        {
            ErrorCode = errorCode;
        }

        public IGPMultiplayerException(string message, Exception innerException)
            : base(message, innerException) { }

        public IGPMultiplayerException(string message, string errorCode, Exception innerException)
            : base(message, innerException)
        {
            ErrorCode = errorCode;
        }
    }

    internal static class IGPMultiplayerLog
    {
        private static int configuredLevel = (int)IGPMultiplayerLogLevel.Off;

        internal static string RunId { get; } = Guid.NewGuid().ToString("N");

        internal static void Configure(IGPMultiplayerLogLevel level) =>
            Volatile.Write(ref configuredLevel, (int)level);

        internal static bool ShouldLog(IGPMultiplayerLogLevel messageLevel)
        {
            var level = (IGPMultiplayerLogLevel)Volatile.Read(ref configuredLevel);
            return level != IGPMultiplayerLogLevel.Off &&
                   messageLevel != IGPMultiplayerLogLevel.Off &&
                   (int)messageLevel <= (int)level;
        }

        internal static void Debug(string scope, string path, string message) =>
            Write(IGPMultiplayerLogLevel.Debug, scope, path, message);

        internal static void Info(string scope, string path, string message) =>
            Write(IGPMultiplayerLogLevel.Info, scope, path, message);

        internal static void Warning(string scope, string path, string message) =>
            Write(IGPMultiplayerLogLevel.Warning, scope, path, message);

        internal static void Error(string scope, string path, string message) =>
            Write(IGPMultiplayerLogLevel.Error, scope, path, message);

        internal static string FormatValue(string? value) =>
            string.IsNullOrWhiteSpace(value) ? "-" : value!;

        private static void Write(IGPMultiplayerLogLevel level, string scope, string path, string message)
        {
            if (!ShouldLog(level)) return;
            string formatted =
                $"[IGP Multiplayer] timestamp={DateTimeOffset.Now:O} runId={RunId} level={level} " +
                $"scope={FormatValue(scope)} path={FormatValue(path)} {message}";
            switch (level)
            {
                case IGPMultiplayerLogLevel.Error:
                    UnityEngine.Debug.LogError(formatted);
                    break;
                case IGPMultiplayerLogLevel.Warning:
                    UnityEngine.Debug.LogWarning(formatted);
                    break;
                default:
                    UnityEngine.Debug.Log(formatted);
                    break;
            }
        }
    }
}
