using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using System.Threading;

namespace Siger.Middlelayer.Common.Log
{
    public class TxtLogEngine : LogEngine
    {
        private const string InfoTag = "I ; ";
        private const string DebguTag = "D ; ";
        private const string WarnTag = "W ; ";
        private const string ErrorTag = "E ; ";
        private const string VerboseTag = "V ; ";

        private bool _disposed;
        private readonly string _logDirectory;

        private readonly ConcurrentQueue<Action> _writeLogQueue;
        private readonly ManualResetEvent _hasLogEvent = new ManualResetEvent(false);

        ~TxtLogEngine()
        {
            DoDispose();
        }

        private void DoDispose()
        {
            if (!_disposed)
            {
                foreach (var write in _writeLogQueue)
                {
                    write();
                }
                _disposed = true;
            }
        }

        public TxtLogEngine(Module module): base(module)
        {
            _writeLogQueue = new ConcurrentQueue<Action>();

            _logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TxtLogs", module.ToString());
            if (!Directory.Exists(_logDirectory))
            {
                Directory.CreateDirectory(_logDirectory);
            }

            var loggingThread = new Thread(Process)
            {
                IsBackground = true
            };
            loggingThread.Start();
        }

        public override void Write(LogLevel level, string msg)
        {
            try
            {
                WriteLog(level, msg);
            }
            catch (Exception)
            {
                //ignore
            }
        }

        private void Process()
        {
            while (true)
            {
                _hasLogEvent.WaitOne();

                _hasLogEvent.Reset();

                Thread.Sleep(100);

                foreach (var write in _writeLogQueue)
                {
                    write();
                    _writeLogQueue.TryDequeue(out var _);
                }
            }
        }

        private void WriteLog(LogLevel level, string content)
        {
            _writeLogQueue.Enqueue(() => File.AppendAllText(LogFilePath(), FormatLog(level, content)));

            _hasLogEvent.Set();
        }
       
        private string LogFilePath()
        {
            var dateTag = DateTime.Now.ToString("yyyyMMdd");
            return Path.Combine(_logDirectory, $"Log_{dateTag}.log");
        }

        private string FormatLog(LogLevel level, string message)
        {
            var now = DateTime.Now;
            var sb = new StringBuilder();
            sb.Append($"[{now:yyyyMMddHHmmss},{now.Millisecond:d3}]-");
            switch (level)
            {
                case LogLevel.Error:
                    sb.Append(ErrorTag);
                    break;
                case LogLevel.Warn:
                    sb.Append(WarnTag);
                    break;
                case LogLevel.Info:
                    sb.Append(InfoTag);
                    break;
                case LogLevel.Verbose:
                    sb.Append(VerboseTag);
                    break;
                case LogLevel.Debug:
                    sb.Append(DebguTag);
                    break;
            }
            sb.Append(message);
            sb.AppendLine();
            return sb.ToString();
        }

        public override void Dispose()
        {
            DoDispose();
            GC.SuppressFinalize(this);
        }
    }
}
