using CsvHelper;
using log4net;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VIZ.Framework.Core;
using VIZ.Package.Domain;
using VIZ.Package.Storage;

namespace VIZ.Package.Service
{
    /// <summary>
    /// 操作日志写入器
    /// </summary>
    public class RecordLogWriter : IDisposable
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly static ILog log = LogManager.GetLogger(typeof(RecordLogWriter));

        /// <summary>
        /// 操作日志单文件记录条数
        /// </summary>
        private readonly static int LOG_ONE_FILE_MAX_RECORD = ApplicationDomainEx.IniStorage.GetValue<LogConfig, int>(p => p.LOG_ONE_FILE_MAX_RECORD);

        /// <summary>
        /// 日志队列
        /// </summary>
        private System.Collections.Concurrent.ConcurrentQueue<RecordLog> recordQueue = new System.Collections.Concurrent.ConcurrentQueue<RecordLog>();

        /// <summary>
        /// 任务信息
        /// </summary>
        private TaskInfo taskInfo;

        /// <summary>
        /// 任务
        /// </summary>
        private Task task;

        /// <summary>
        /// 写入流
        /// </summary>
        private StreamWriter streamWriter;

        /// <summary>
        /// CSV写入
        /// </summary>
        private CsvWriter csvWriter;

        /// <summary>
        /// 写入数量
        /// </summary>
        private int writeCount;

        /// <summary>
        /// 写入锁对象
        /// </summary>
        private object write_lock_object = new object();

        /// <summary>
        /// 工作文件夹
        /// </summary>
        public string WorkFolder { get; private set; }

        /// <summary>
        /// 操作日志写入器
        /// </summary>
        /// <param name="workFolder">工作文件夹</param>
        public RecordLogWriter(string workFolder)
        {
            this.WorkFolder = workFolder;

            this.streamWriter = new StreamWriter(this.GetFileName(), false, Encoding.Default);
            this.csvWriter = new CsvWriter(this.streamWriter, CultureInfo.InvariantCulture);
            this.csvWriter.WriteHeader<RecordLog>();
            this.csvWriter.NextRecord();
        }

        /// <summary>
        /// 追加日志
        /// </summary>
        public void AppendLog(RecordLog log)
        {
            this.recordQueue.Enqueue(log);
        }

        /// <summary>
        /// 开始写入
        /// </summary>
        public void BeginWrite()
        {
            this.taskInfo = new TaskInfo();
            this.task = ThreadHelper.SafeRun(this.ExecuteWrite);
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            if (this.taskInfo != null)
            {
                this.taskInfo.IsCancel = true;
            }

            this.taskInfo = null;
            this.task = null;

            // 写入日志
            this.Flush();

            this.csvWriter?.Dispose();
            this.streamWriter?.Dispose();
        }

        /// <summary>
        /// 执行写入
        /// </summary>
        private void ExecuteWrite()
        {
            TaskInfo info = this.taskInfo;

            while (!info.IsCancel)
            {
                System.Threading.Thread.Sleep(30 * 1000);

                if (this.taskInfo == null || this.taskInfo.IsCancel)
                    return;

                this.Flush();
            }
        }

        /// <summary>
        /// 将日志写入文件
        /// </summary>
        private void Flush()
        {
            lock (this.write_lock_object)
            {
                while (this.recordQueue.Count > 0)
                {
                    if (!this.recordQueue.TryDequeue(out RecordLog record))
                    {
                        System.Threading.Thread.Sleep(1000);
                        continue;
                    }

                    try
                    {
                        this.csvWriter.WriteRecord(record);
                        this.csvWriter.NextRecord();

                        ++this.writeCount;

                        if (this.writeCount >= LOG_ONE_FILE_MAX_RECORD)
                        {
                            this.csvWriter.Flush();
                            this.csvWriter.Dispose();
                            this.streamWriter.Dispose();

                            this.streamWriter = new StreamWriter(this.GetFileName(), false, Encoding.Default);
                            this.csvWriter = new CsvWriter(this.streamWriter, CultureInfo.InvariantCulture);
                            this.csvWriter.WriteHeader<RecordLog>();
                            this.csvWriter.NextRecord();
                            this.writeCount = 0;
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex);
                    }
                }
            }
        }

        /// <summary>
        /// 获取日志文件路径
        /// </summary>
        /// <returns>日志文件路径</returns>
        private string GetFileName()
        {
            return Path.Combine(this.WorkFolder, $"{ DateTime.Now.ToString("yyyy_MM_dd__HH_mm_ss__fffffff")}.csv");
        }
    }
}
