#if NETCOREAPP
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks.Dataflow;
using Newtonsoft.Json;
using UtilityShared;

public class GameLogWriter : TextWriter
{
    public override Encoding Encoding => standStreamWriter.Encoding;
    public static GameLogWriter Instamce { get; private set; }

    private const string LogPath = "logs";
    private const long LogSize = 1024 * 1024 * 50; // 50mb
    private const string TempFile = "sendQueue.temp";
    public const string TimeFormat = "yyyy-MM-dd-HH";

    private string assemblyName { get; } = Assembly.GetEntryAssembly()?.GetName().Name;
    private readonly string m_UploadUrl;
    private string m_CurLogDate = "";
    private int m_CurLogIndex;
    private string m_CurLogName = "";
    private readonly HashSet<string> m_SendQueue;
    private readonly ActionBlock<string> m_LogSendBlock;
    private StreamWriter standStreamWriter { get; } = new StreamWriter(Console.OpenStandardOutput());
    private StreamWriter m_FileStreamWriter;
    private StreamWriter fileStreamWriter
    {
        get
        {
            var logName = GetCurLogName();
            if (!m_CurLogName.Equals(logName))
            {
                var oldLogName = m_CurLogName;
                m_CurLogName = logName;
                if (!Directory.Exists(LogPath))
                    Directory.CreateDirectory(LogPath);
                if (m_FileStreamWriter != null)
                {
                    m_FileStreamWriter.Flush();
                    m_FileStreamWriter.Close();
                    m_FileStreamWriter.Dispose();
                }
                m_FileStreamWriter = File.AppendText($"{LogPath}/{m_CurLogName}");
                try
                {
                    OnLogFileChange(oldLogName, m_CurLogName);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
            return m_FileStreamWriter;
        }
    }

    public GameLogWriter()
    {
        var curPath = AppDomain.CurrentDomain.BaseDirectory;
        var configPath = curPath.Contains("Debug") ? $"../../../../Config/config.txt" : "../Config/config.txt";
        configPath = Path.Combine(curPath, configPath);
        var configLines = File.ReadAllLines(configPath).Where(x => !x.Contains("#"));
        var configData = string.Join("", configLines);
        var config = JsonConvert.DeserializeObject<Dictionary<string, object>>(configData);
        m_UploadUrl = config.ContainsKey("LogUploadUrl") ? config["LogUploadUrl"].ToString() : null;

        Instamce = this;

        if (string.IsNullOrWhiteSpace(m_UploadUrl))
            return;

        m_LogSendBlock = new ActionBlock<string>(LogSend);

        if (!File.Exists(TempFile))
        {
            m_SendQueue = new HashSet<string>();
            return;
        }

        var oldFiles = File.ReadAllLines(TempFile).Where(File.Exists);
        m_SendQueue = new HashSet<string>(oldFiles);
        foreach (var file in m_SendQueue)
        {
            m_LogSendBlock.Post(file);
        }
    }

    private string GetCurLogName()
    {
        // 1 按时间变更
        var logDate = DateTime.Now.ToString(TimeFormat);
        if (!m_CurLogDate.Equals(logDate))
        {
            m_CurLogDate = logDate;
            m_CurLogIndex = 0;
        }
        // 2 按大小变更
        else if (m_FileStreamWriter.BaseStream.Length >= LogSize)
            m_CurLogIndex++;

        return $"{logDate}_{m_CurLogIndex}.log";
    }

    public override void Write(string value)
    {
        standStreamWriter.Write(value);
        standStreamWriter.Flush();
        fileStreamWriter.Write(value);
        fileStreamWriter.Flush();
    }

    public override void WriteLine(string value)
    {
        standStreamWriter.WriteLine(value);
        standStreamWriter.Flush();
        fileStreamWriter.WriteLine(value);
        fileStreamWriter.Flush();
    }

    private void OnLogFileChange(string oldLogName, string curLogName)
    {
        var time = DateTime.Now.AddDays(-15);
        var files = Directory.GetFiles(LogPath)
            .Where(x => File.GetCreationTime(x) < time);
        foreach (var file in files)
        {
            try
            {
                File.Delete(file);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        SendLogFile(oldLogName);
    }

    private void SendLogFile(params string[] files)
    {
        foreach (var file in files)
        {

            if (string.IsNullOrWhiteSpace(m_UploadUrl) || string.IsNullOrWhiteSpace(file))
                return;

            m_SendQueue.Add(file);
            m_LogSendBlock.Post(file);
        }
        File.WriteAllLines(TempFile, m_SendQueue);
    }

    private void LogSend(string file)
    {
        try
        {
            var data = new NameValueCollection();
            data["processName"] = assemblyName;
            var resp = HttpHelper.HttpUploadFile(m_UploadUrl, Path.Combine(LogPath, file), data);
            var files = JsonConvert.DeserializeObject<List<string>>(resp);
            if (!files.Contains(file))
            {
                m_LogSendBlock.Post(file);
                GameDebug.Log($"send log file {file} failed to url: {m_UploadUrl}");
                return;
            }
            m_SendQueue.Remove(file);
            File.WriteAllLines(TempFile, m_SendQueue);
            GameDebug.Log($"send log file {file} success to url: {m_UploadUrl}");
        }
        catch (Exception e)
        {
            GameDebug.LogError($"send log file {file} error to url: {m_UploadUrl}, error {e}");
        }
    }

    public void SendCurrentLog(DateTime? start, DateTime? end)
    {
        if (start.HasValue && end.HasValue)
        {
            var cur = start.Value;
            var filters = new List<string>();
            while (cur >= start && cur <= end)
            {
                filters.Add(cur.ToString(TimeFormat));
                cur = cur.AddHours(1);
            }

            var files = Directory.GetFiles(LogPath)
                .Select(Path.GetFileName)
                .Where(f => filters.Any(f.Contains))
                .ToArray();
            SendLogFile(files);
            GameDebug.Log($"pull current logs {string.Join(',', files)} to url: {m_UploadUrl}");
        }
        else
        {
            var curLog = GetCurLogName();
            SendLogFile(curLog);
            GameDebug.Log($"pull current log {curLog} to url: {m_UploadUrl}");
        }
    }
}
#endif