using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace Jovian.Logger {
    public static class LoggerUtility {
        private static LoggerSettings loggerSettings;
        private static bool setByFrameCount = true;
        private static int frameCount;
        private static bool isLoaded;
        private static readonly int mainThreadId = Thread.CurrentThread.ManagedThreadId;

        /// <summary>
        /// Returns true if the current code is executing on the main Unity thread.
        /// </summary>
        public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId;

        /// <summary>
        /// Callback to post formatted log messages.
        /// </summary>
        public static Action<(JovianLogType, LogCategory, string)> FormattedLogCallback { get; set; }

        /// <summary>
        /// Callback for watch-mode logs that update in place instead of creating new entries.
        /// Parameters: (watchKey, value, logCategory)
        /// </summary>
        public static Action<(string key, string value, LogCategory category)> WatchCallback { get; set; }

        /// <summary>
        /// Callback when a watch key is removed.
        /// </summary>
        public static Action<string> UnwatchCallback { get; set; }

        /// <summary>
        /// If enabled it will show either the Unity's Time.frameCount or the custom frame count set by the user. This is a global setting.
        /// </summary>
        public static int FrameCount {
            get {
                if(setByFrameCount) {
                    try {
                        frameCount = Time.frameCount;
                    } catch(UnityException) {
                        // Time.frameCount can only be called from the main thread.
                        // Return last known value when called from a background thread.
                    }
                }

                return frameCount;
            }
            set {
                frameCount = value;
                setByFrameCount = false;
            }
        }

        public static LoggerSettings LoadCustomLoggerSettings() {
            try {
                if(isLoaded) {
                    return loggerSettings;
                }

                loggerSettings = Resources.Load<LoggerSettings>("logger-settings");
                isLoaded = true;
                return loggerSettings;
            } catch {
                //Debug.Log($"[Exception] LoggerSettings could not be loaded.");
            }

            return null;
        }

        /// <summary>
        /// Toggle the frame counting feature to be included with the logs on/off
        /// </summary>
        /// <param name="enable"></param>
        public static void ToggleFrameCount(bool enable) {
            IsFrameCountEnabled = enable;
        }

        /// <summary>
        /// Enable/Disable logging globally
        /// </summary>
        /// <param name="enable"></param>
        public static void ToggleLogging(bool enable) {
            loggerSettings.enableGlobalLogging = enable;
        }

        /// <summary>
        /// Check if the frame count feature is enabled
        /// </summary>
        public static bool IsFrameCountEnabled { get; private set; }

        /// <summary>
        /// Load settings preemptively to avoid asynchronous loading for unity assets.
        /// </summary>
        public static void PreloadLoggerSettings() {
            try {
                LoadCustomLoggerSettings();
            } catch(Exception e) {
                Debug.Log($"[Exception] LoggerSettings could not be loaded. {e}");
            }
        }

        /// <summary>
        /// Adds a custom filter programmatically. Useful for setting up remote filters
        /// </summary>
        /// <param name="logCategory">Required set of log categories</param>
        /// <param name="jovianLogLevel">The log level, default being level 4, Exception(in unity it is the base log level)</param>
        /// <param name="callerNames">A list of classes that should be watched, default null</param>
        /// <param name="clearAll">If all preexisting filters should be first removed</param>
        public static void AddFilter(LogCategory logCategory, JovianLogType jovianLogLevel = JovianLogType.Spam, List<string> callerNames = null, bool clearAll = false) {
            var filter = new Filters() {
                jovianLogType = jovianLogLevel,
                logCategory = logCategory,
                callerNames = callerNames
            };
            var loggerSettings = LoadCustomLoggerSettings();
            if(clearAll) {
                loggerSettings.globalFilters = new Filters[1];
                loggerSettings.globalFilters[0] = filter;
                InternalLogger.LoadSettings();
                return;
            }

            var filters = new Filters[loggerSettings.globalFilters.Length + 1];
            for(var i = 0; i < loggerSettings.globalFilters.Length; i++) {
                filters[i] = loggerSettings.globalFilters[i];
            }

            filters[^1] = filter;
            loggerSettings.globalFilters = filters;
            InternalLogger.LoadSettings();
        }

        public static void ReloadLoggerSettings() {
            InternalLogger.LoadSettings();
        }

        public static UnityEngine.LogType GetLogType(JovianLogType jovianLogType) {
            return jovianLogType switch {
                JovianLogType.Info => UnityEngine.LogType.Log,
                JovianLogType.Warning => UnityEngine.LogType.Warning,
                JovianLogType.Error => UnityEngine.LogType.Error,
                JovianLogType.Assert => UnityEngine.LogType.Assert,
                JovianLogType.Exception => UnityEngine.LogType.Exception,
                JovianLogType.Spam => UnityEngine.LogType.Log,
                _ => UnityEngine.LogType.Log
            };
        }
    }
}
