using DH.Commons.Helper;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static DH.Commons.Enums.EnumHelper;

namespace DH.Commons.Enums
{
    public interface ILogOutput
    {
        event Action<LogMsg> OnLogMsgOutput;
        void LogDisplay(LogMsg msg);
    }
    public interface ILogger
    {
        event Action<LogMsg> OnLog;
        LoggerHelper LoggerHelper { get; set; }
        //void LogAsync(DateTime dt, LogLevel loglevel, string msg);
        void LogAsync(LogMsg msg);
    }
    public class LoggerHelper
    {
        public event Action<DateTime, string> OnLogExceptionRaised;

        public string LogPath { get; set; }
        public string LogPrefix { get; set; }

        LogLevel LogLevel = LogLevel.Information;

        public LoggerHelper() { }
        public LoggerHelper(string logPath, string logPrefix, LogLevel logLevel = LogLevel.Information)
        {
            LogPath = logPath;
            LogPrefix = logPrefix;

            LogLevel = logLevel;
        }

        public void SetLogLevel(LogLevel logLevel)
        {
            if (LogLevel != logLevel)
                LogLevel = logLevel;
        }
        ////耗时操作从 _taskFactory分配线程
        //public TaskFactory _taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning);
        readonly ConcurrentQueue<LogMsg> _logQueue = new ConcurrentQueue<LogMsg>();
        Task _logTask = null;
        readonly object _logLock = new object();

        public async void LogAsync(LogMsg msg)
        {
            await Task.Run(() =>
            {
                _logQueue.Enqueue(msg);

                lock (_logLock)
                {
                    if (_logTask == null)
                    {
                        _logTask = Task.Run(async () =>
                        {
                            string filePath = Path.Combine(LogPath, $"{(string.IsNullOrWhiteSpace(LogPrefix) ? "Log_" : ("Log_" + LogPrefix + "_"))}{DateTime.Now.ToString("yyyyMMdd")}.txt");
                            try
                            {
                                if (!StaticHelper.CheckFilesCanUse(filePath))
                                {
                                    OnLogExceptionRaised?.Invoke(DateTime.Now, $"日志文件{filePath}被占用,无法写入");
                                    return;
                                }
                                using (StreamWriter writer = new StreamWriter(filePath, true, System.Text.Encoding.UTF8))
                                {
                                    while (true)
                                    {
                                        if (!Directory.Exists(LogPath))
                                        {
                                            Directory.CreateDirectory(LogPath);
                                        }

                                        while (_logQueue.Count > 0)
                                        {
                                            if (_logQueue.TryDequeue(out LogMsg log))
                                            {
                                                if (log.LogLevel >= LogLevel)
                                                {
                                                    writer.WriteLine($"{log.LogTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}[{log.ThreadId}]\t{log.LogLevel.GetEnumDescription()}\t{log.Msg}");
                                                }
                                            }
                                        }
                                        writer.Flush();

                                        await Task.Delay(2000);
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                //OnLogExceptionRaised?.Invoke(DateTime.Now, $"日志文件{filePath}写入异常:/*{ex.GetExceptionMessage()*/}");
                                OnLogExceptionRaised?.Invoke(DateTime.Now, $"日志文件{filePath}写入异常");
                            }
                        });
                    }
                }
            });
        }

        public void LogAsync(DateTime dt, LogLevel logLevel, string msg)
        {
            LogAsync(new LogMsg(dt, logLevel, msg));
        }
    }

    public class LogMsg
    {
        public DateTime LogTime { get; set; }
        public LogLevel LogLevel { get; set; }
        //public string Prefix { get; set; }
        public string Msg { get; set; }

        public string MsgSource { get; set; }

        public int ThreadId { get; set; }

        public LogMsg() { }
        public LogMsg(DateTime dt, LogLevel logLevel, string msg)
        {
            LogTime = dt;
            LogLevel = logLevel;
            Msg = msg;
        }

        public override string ToString()
        {
            return $"{LogTime.ToString("HH:mm:ss.fff")}\t{MsgSource}\t{Msg}";
        }
    }
}