using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace Jovian.Logger {
    internal class LoggerSettingsData {
        public bool enableGlobalLogging = true;
        public Filters[] globalFilters = Array.Empty<Filters>();
        public LoggerColors loggerColors = new();
        public bool isLoaded = false;
#if UNITY_EDITOR
        public List<Filters> LocalFilters { get; set; } = new();
#endif
    }

    internal static class InternalLogger {
        private static readonly LoggerSettingsData loggerSettingsData = new();
        private static bool enableGlobalLogging = true;
        internal static bool IsMainThread => LoggerUtility.IsMainThread;

        internal static void LoadSettings() {
            var loggerSettings = LoggerUtility.LoadCustomLoggerSettings();

            if(!loggerSettings) {
                return;
            }

            var colors = loggerSettings.loggerColors;
            loggerSettingsData.globalFilters = loggerSettings.globalFilters;
            enableGlobalLogging = loggerSettings.enableGlobalLogging;
            loggerSettingsData.loggerColors.infoColor = colors.infoColor;
            loggerSettingsData.loggerColors.warningColor = colors.warningColor;
            loggerSettingsData.loggerColors.errorColor = colors.errorColor;
            loggerSettingsData.loggerColors.assertColor = colors.assertColor;
            loggerSettingsData.loggerColors.exceptionColor = colors.exceptionColor;
            loggerSettingsData.loggerColors.spamColor = colors.spamColor;

#if UNITY_EDITOR
            if(!Environment.CommandLine.Contains("-batchmode")) {
                loggerSettingsData.LocalFilters = loggerSettings.LocalFilters;
            } else {
                loggerSettings.LocalFilters.Clear();
            }

            Debug.Log("[CustomLogger] Local Filters: " + loggerSettingsData.LocalFilters.Count);
#endif
            loggerSettingsData.isLoaded = true;
            Debug.Log("[CustomLogger] Global Filters: " + loggerSettingsData.globalFilters.Length);
            Debug.Log("[CustomLogger] Settings loaded");
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void LogInternal(JovianLogType jovianLogType, LogCategory logcat, string classType, string msg, Color color, string args = "", UnityEngine.Object reference = null) {
            if(!enableGlobalLogging) {
                return;
            }

            if(!loggerSettingsData.isLoaded && IsMainThread) {
                LoadSettings();
            }

            // Check if any filters are present and apply filters
            foreach(var filter in loggerSettingsData.globalFilters) {
                if(filter == null) {
                    continue;
                }

                if((filter.jovianLogType < jovianLogType || !filter.logCategory.HasFlag(logcat)) || !FilterCaller(filter.callerNames, filter.callerListingType)) {
                    return;
                }
            }

#if UNITY_EDITOR
            if(!Environment.CommandLine.Contains("-batchmode")) {
                foreach(var filter in loggerSettingsData.LocalFilters) {
                    if(filter == null) {
                        continue;
                    }

                    if((filter.jovianLogType < jovianLogType || !filter.logCategory.HasFlag(logcat)) || !FilterCaller(filter.callerNames, filter.callerListingType)) {
                        return;
                    }
                }
            } else {
                if(jovianLogType == JovianLogType.Spam) {
                    return;
                }
            }
#endif
            StringBuilder sb = new(500);
            switch(jovianLogType) {
                case JovianLogType.Spam:
                    sb.Append("SPAM -> ");
                    break;
                case JovianLogType.Info:
                    sb.Append("INFO -> ");
                    break;
                case JovianLogType.Warning:
                    sb.Append("WARNING -> ");
                    break;
                case JovianLogType.Error:
                    sb.Append("ERROR -> ");
                    break;
                case JovianLogType.Assert:
                    sb.Append("ASSERT -> ");
                    break;
                case JovianLogType.Exception:
                    sb.Append("EXCEPTION -> ");
                    break;
                default:
                    return;
            }

            var isFrameCountEnabled = LoggerUtility.IsFrameCountEnabled;

            // As exceptions can be reported to Unity Exception tracking, we do not want frame numbers in the message. It will prevent Unity from grouping reports
            var logTypeShouldIncludeFrameCount = jovianLogType != JovianLogType.Exception;
            if(isFrameCountEnabled && logTypeShouldIncludeFrameCount) {
                sb.Append("F:");
                sb.Append(LoggerUtility.FrameCount);
                sb.Append(" |");
            }

            sb.Append(" [");
            sb.Append(logcat);
            sb.Append("] ");
            sb.Append("[");
            sb.Append(classType);
            sb.Append("] ");
            sb.Append(msg);
            var message = sb.ToString();
            LoggerUtility.FormattedLogCallback?.Invoke((jovianLogType, logcat, message));

#if UNITY_EDITOR
            //remove the color when not in the editor to avoid cluttering the log files
            if(!Environment.CommandLine.Contains("-batchmode") && IsMainThread) {
                message = $"<color=#{ColorUtility.ToHtmlStringRGB(color)}>{message}</color>";
            }
#endif
            Debug.unityLogger.Log(LoggerUtility.GetLogType(jovianLogType), (object)message, reference);
            return;

            bool FilterCaller(List<string> filterCallerNames, CallerListingType filterCallerListingType) {
                foreach(var caller in filterCallerNames) {
                    if(!string.IsNullOrEmpty(caller)) {
                        switch(filterCallerListingType) {
                            case CallerListingType.Blacklist_Caller:
                                return !classType.Contains(caller);
                            case CallerListingType.Whitelist_Caller:
                                return classType.Contains(caller);
                        }
                    }
                }

                return true;
            }
        }

        [Conditional("UNITY_EDITOR")] [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogSpam(string caller, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Spam, logcat, caller, msg, loggerSettingsData.loggerColors.spamColor, "spam", reference);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogInfo(string caller, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Info, logcat, caller, msg, loggerSettingsData.loggerColors.infoColor, "", reference);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogWarning(string classType, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Warning, logcat, classType, msg, loggerSettingsData.loggerColors.warningColor, "", reference);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogError(string caller, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Error, logcat, caller, msg, loggerSettingsData.loggerColors.errorColor, "", reference);
        }

        internal static void LogException(string caller, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Exception, logcat, caller, msg, loggerSettingsData.loggerColors.exceptionColor, "", reference);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogAssert(string caller, string msg, LogCategory logcat, UnityEngine.Object reference = null) {
            LogInternal(JovianLogType.Assert, logcat, caller, msg, loggerSettingsData.loggerColors.assertColor, "", reference);
        }
    }
}
