using Apewer.Internals;
using Apewer.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

using static System.ConsoleColor;

namespace Apewer
{

    /// <summary>日志记录程序。</summary>
    public sealed class Logger
    {

        private bool _useconsole = true;
        private bool _usefile = false;
        private bool _uselock = false;

        /// <summary>当前日志记录器的名称。</summary>
        public string Name { get; set; }

        /// <summary>已启用。</summary>
        public bool Enabled { get; set; }

        /// <summary>在后台线程处理日志。默认值:FALSE。</summary>
        internal bool Background { get; set; } = false;

        /// <summary>使用控制台输出。默认值:TRUE。</summary>
        public bool UseConsole
        {
            get { return _useconsole; }
            set { if (!_uselock) _useconsole = value; }
        }

        /// <summary>使用日志文件。默认值:FALSE。</summary>
        public bool UseFile
        {
            get { return _usefile; }
            set { if (!_uselock) _usefile = value; }
        }

        /// <summary>异常。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<Exception> OnException { get; set; }

        /// <summary>错误。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<string> OnError { get; set; }

        /// <summary>注意。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<string> OnWarning { get; set; }

        /// <summary>注意。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<string> OnInfo { get; set; }

        /// <summary>文本。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<string> OnText { get; set; }

        /// <summary>调试。设置处理方法以替代 UseConsole 和 UseFile。</summary>
        public Event<string> OnDebug { get; set; }

        private void Invoke(Action action)
        {
            if (Background)
            {
                RuntimeUtility.InBackground(action, true);
                return;
            }
            try { action.Invoke(); } catch { }
        }

        private void Colorful(object sender, string tag, Nullable<ConsoleColor> color, object[] content, Exception exception, Event<string> defined)
        {
            if (!Enabled) return;
            Invoke(() =>
            {
                if (defined != null)
                {
                    defined.Invoke(sender, MergeContent(content));
                    return;
                }

                var item = new LogItem();
                item.Sender = sender;
                item.Tag = tag;
                item.Color = color;
                item.Content = MergeContent(content);
                item.Exception = exception;

                if (UseConsole) ToConsole(item);
                if (UseFile) ToFile(item, this);
            });
        }

        /// <summary>记录异常。</summary>
        internal void InnerException(object sender, Exception exception)
        {
            if (!Enabled) return;
            var type = null as string;
            var content = null as string;
            if (exception != null)
            {
                try
                {
                    type = exception.GetType().FullName;
                    content = MergeContent(type, exception.Message);
                }
                catch { }
            }
            if (content == null) content = "无效的 Exception 实例。";
            Colorful(sender, "Exception", DarkMagenta, new object[] { type, content }, exception, OnError);
        }

        /// <summary>记录错误。多个 Content 参数将以“ | ”分隔。</summary>
        internal void InnerError(object sender, object[] content) => Colorful(sender, "Error", DarkRed, content, null, OnError);

        /// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
        internal void InnerWarning(object sender, object[] content) => Colorful(sender, "Warning", DarkYellow, content, null, OnWarning);

        /// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
        internal void InnerInfo(object sender, object[] content) => Colorful(sender, "Info", DarkBlue, content, null, OnInfo);

        /// <summary>记录文本。多个 Content 参数将以“ | ”分隔。</summary>
        internal void InnerText(object sender, object[] content) => Colorful(sender, "Text", null, content, null, OnText);

        /// <summary>记录调试。多个 Content 参数将以“ | ”分隔。</summary>
        [Conditional("DEBUG")]
        internal void InnerDebug(object sender, object[] content) => Colorful(sender, "Debug", null, content, null, OnDebug);

        private void Write(object sender, object[] content) => Colorful(sender, null, null, content, null, null);

        /// <summary>创建新实例。</summary>
        public Logger()
        {
            Enabled = true;
        }

        private Logger(string name, bool useConsole, bool useFile, bool enabled)
        {
            Name = name;
            UseConsole = useConsole;
            UseFile = useFile;
            Enabled = enabled;
        }

        #region 输出。

        internal static object FileLocker = new object();
        internal static object ConsoleLocker = new object();

        /// <summary>获取用于保存日志文件的路径。</summary>
        public static Func<Logger, string> FilePathGetter { get; set; }

        private static string MergeContent(params object[] content) => TextUtility.Join(" | ", content);

        private static string FormatSender(object sender)
        {
            if (sender == null) return null;
            if (sender is string) return sender as string;
            if (sender is Type) return ((Type)sender).FullName;
            return sender.GetType().FullName;
        }

        // 向控制台输出。
        private static void ToConsole(LogItem item)
        {
            var hasTag = !string.IsNullOrEmpty(item.Tag);
            var sender = FormatSender(item.Sender);
            var colorful = item.Color != null;
            lock (ConsoleLocker)
            {
                if (!colorful)
                {
                    System.Console.WriteLine(ToText(item));
                    return;
                }

                System.Console.ResetColor();

                System.Console.ForegroundColor = DarkGray;
                System.Console.Write(item.Clock);

                System.Console.ResetColor();

                if (hasTag)
                {
                    System.Console.Write("  ");
                    if (item.Color != null)
                    {
                        System.Console.BackgroundColor = item.Color.Value;
                        System.Console.ForegroundColor = White;
                    }
                    System.Console.Write(" ");
                    System.Console.Write(item.Tag);
                    System.Console.Write(" ");
                }

                System.Console.ResetColor();

                if (!string.IsNullOrEmpty(sender))
                {
                    System.Console.Write("  <");
                    System.Console.Write(sender);
                    System.Console.Write(">");
                }

                if (!string.IsNullOrEmpty(item.Content))
                {
                    System.Console.Write("  ");
                    System.Console.Write(item.Content);
                }

                System.Console.WriteLine();
            }
        }

        private static string ToText(LogItem item)
        {
            var sb = new StringBuilder();
            var sender = FormatSender(item.Sender);
            sb.Append(item.Clock);
            if (!string.IsNullOrEmpty(item.Tag))
            {
                sb.Append("  [");
                sb.Append(item.Tag);
                sb.Append("]");
            }
            if (!string.IsNullOrEmpty(sender))
            {
                sb.Append("  <");
                sb.Append(sender);
                sb.Append(">");
            }
            if (!string.IsNullOrEmpty(item.Content))
            {
                sb.Append("  ");
                sb.Append(item.Content);
            }
            return sb.ToString();
        }

        // 向日志文件输出文本,文件名按日期自动生成。
        private static string ToFile(string plain, Logger logger)
        {
            var text2 = TextUtility.Trim(plain) + "\r\n";
            lock (FileLocker)
            {
                var path = GetFilePath(logger);
                if (string.IsNullOrEmpty(path)) return "写入日志文件失败:无法获取日志文件路径。";

                var bytes = TextUtility.Bytes(TextUtility.Merge(plain, "\r\n"));
                if (!StorageUtility.AppendFile(path, bytes)) return "写入日志文件失败。";
            }
            return null;
        }

        // 向日志文件输出文本,文件名按日期自动生成。
        private static string ToFile(LogItem item, Logger logger) => ToFile(ToText(item), logger);

        /// <summary>获取日志文件路径发生错误时返回 NULL 值。</summary>
        /// <remarks>默认例:<br/>d:\app\log\1970-01-01.log<br/>d:\www\app_data\log\1970-01-01.log</remarks>
        public static string GetFilePath(Logger logger = null)
        {
            var getter = FilePathGetter;
            if (getter != null) try { return getter.Invoke(logger); } catch { }

            // 找到 App_Data 目录。
            var appDir = RuntimeUtility.ApplicationPath;
            var dataDir = Path.Combine(appDir, "app_data");
            if (StorageUtility.DirectoryExists(dataDir)) appDir = dataDir;

            // 检查 Log 目录,不存在时创建,创建失败时返回。
            var logDir = Path.Combine(appDir, "log");
            if (!StorageUtility.AssureDirectory(logDir)) return null;

            // 文件不存在时创建新文件,无法创建时返回。
            var date = DateTime.Now.Lucid(true, false, false, false);
            var filePath = Path.Combine(logDir, date + ".log");
            if (!StorageUtility.FileExists(filePath))
            {
                StorageUtility.WriteFile(filePath, TextUtility.Bom);
                if (!StorageUtility.FileExists(filePath)) return null;
            }

            // 返回 log 文件路径。
            return filePath;
        }

        #endregion

        #region 默认实列。

        private static Logger _default = new Logger("Apewer.Logger.Default", true, false, true);
        private static Logger _console = new Logger("Apewer.Logger.Console", true, false, true);
        private static Logger _web = new Logger("Apewer.Logger.Web", true, false, true);

#if DEBUG
        internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, true, true);
#else
        internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, false, false);
#endif

        /// <summary>内部的日志记录程序。</summary>
        public static Logger Internals { get => _internals; }

        /// <summary>默认的日志记录程序。</summary>
        public static Logger Default { get => _default; }

        /// <summary>仅输出到控制台的日志记录程序。</summary>
        public static Logger Console { get => _console; }

        /// <summary>用于 Web 的日志记录程序。</summary>
        public static Logger Web { get => _web; }

        #endregion

        #region 静态调用。

        /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary>
        public static void Write(params object[] content) => Default.Write(null, content);

        /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary>
        public static void Write<T>(params object[] content) => Default.Write(typeof(T), content);

        /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期。</summary>
        public static void Write(Exception exception) => Default.InnerException(null, exception);

        /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期。</summary>
        public static void Write<T>(Exception exception) => Default.InnerException(typeof(T), exception);

        #endregion

    }

}