using System.IO.Compression;
using EonaCat.Logger.EonaCatCoreLogger;
using EonaCat.Logger.Extensions;
using EonaCat.Logger.Managers;

namespace EonaCat.Logger.Test.Web;

public class Logger
{
    private LogManager _logManager;
    public LoggerSettings LoggerSettings { get; }
    public bool UseLocalTime { get; set; }
    public string LogFolder { get; set; } = Path.Combine(FileLoggerOptions.DefaultPath, "logs");
    public string CurrentLogFile => _logManager.CurrentLogFile;
    public bool IsDisabled { get; set; }

    /// <summary>
    /// Logger
    /// </summary>
    /// <param name="name"></param>
    /// <param name="typesToLog"></param>
    /// <param name="useLocalTime"></param>
    /// <param name="maxFileSize"></param>
    public Logger(string name = "EonaCatTestLogger", List<ELogType> typesToLog = null, bool useLocalTime = false, int maxFileSize = 20_000_000)
    {
        UseLocalTime = useLocalTime;

        LoggerSettings = new LoggerSettings
        {
            Id = name,
            TypesToLog = typesToLog,
            UseLocalTime = UseLocalTime,
            FileLoggerOptions =
            {
                LogDirectory = LogFolder,
                FileSizeLimit = maxFileSize,
                UseLocalTime = UseLocalTime,
            },
        };
    }

    public void DeleteCurrentLogFile()
    {
        if (IsDisabled)
        {
            return;
        }

        _logManager.DeleteCurrentLogFile();
    }

    private string ConvertToAbsolutePath(string path)
    {
        return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path);
    }

    public async Task DownloadLogAsync(HttpContext context, string logName, long limit)
    {
        var logFileName = logName + ".log";

        await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open,
            FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
        var response = context.Response;

        response.ContentType = "text/plain";
        response.Headers.ContentDisposition = "attachment;filename=" + logFileName;

        if (limit > fS.Length || limit < 1)
        {
            limit = fS.Length;
        }

        var oFS = new OffsetStream(fS, 0, limit);
        var request = context.Request;
        Stream stream;

        string acceptEncoding = request.Headers["Accept-Encoding"];
        if (string.IsNullOrEmpty(acceptEncoding))
        {
            stream = response.Body;
        }
        else
        {
            var acceptEncodingParts = acceptEncoding.Split(new[] { ',' },
                StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);

            if (acceptEncodingParts.Contains("br"))
            {
                response.Headers.ContentEncoding = "br";
                stream = new BrotliStream(response.Body, CompressionMode.Compress);
            }
            else if (acceptEncodingParts.Contains("gzip"))
            {
                response.Headers.ContentEncoding = "gzip";
                stream = new GZipStream(response.Body, CompressionMode.Compress);
            }
            else if (acceptEncodingParts.Contains("deflate"))
            {
                response.Headers.ContentEncoding = "deflate";
                stream = new DeflateStream(response.Body, CompressionMode.Compress);
            }
            else
            {
                stream = response.Body;
            }
        }

        await using (stream)
        {
            await oFS.CopyToAsync(stream).ConfigureAwait(false);

            if (fS.Length > limit)
            {
                await stream.WriteAsync("\r\n####___TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
            }
        }
    }

    public async Task LogAsync(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true)
    {
        if (IsDisabled)
        {
            return;
        }

        InitLogger();
        await _logManager.WriteAsync(message, logType, writeToConsole).ConfigureAwait(false);
    }

    private void InitLogger()
    {
        if (_logManager == null)
        {
            // Initialize LogManager
            _logManager = new LogManager(LoggerSettings);
            _logManager.Settings.TypesToLog.Clear();
        }
    }

    public async Task LogAsync(Exception exception, string message = "", bool writeToConsole = true)
    {
        if (IsDisabled)
        {
            return;
        }

        InitLogger();
        if (LoggerSettings.TypesToLog.Contains(ELogType.ERROR))
        {
            await _logManager.WriteAsync(exception, message, writeToConsole: writeToConsole).ConfigureAwait(false);
        }
    }
}