using EonaCat.Json.Linq;
using EonaCat.Logger.EonaCatCoreLogger;
using EonaCat.Logger.EonaCatCoreLogger.Models;
using EonaCat.Logger.Servers.GrayLog;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using static EonaCat.Logger.Managers.LogHelper;

namespace EonaCat.Logger.Managers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

/// <summary>
///     Logger settings.
/// </summary>
public class LoggerSettings
{
    public delegate void LogDelegate(EonaCatLogMessage message);

    private ColorSchema _colors = new();
    private bool _enableConsole = true;

    private FileLoggerOptions _fileLoggerOptions;

    private string _headerFormat = "{ts} {tz} {host} {category} {thread} {sev}";
    private string _timestampFormat = "yyyy-MM-dd HH:mm:ss";

    /// <summary>
    ///     Determines if we need to use the local time in the logging or UTC (default:false)
    /// </summary>
    public bool UseLocalTime { get; set; }

    /// <summary>
    /// Gets or sets the unique identifier for the application instance.
    /// </summary>
    public string Id { get; set; } = DllInfo.ApplicationName;

    /// <summary>
    /// Gets or sets the collection of custom header tokens to be included in outgoing requests.
    /// </summary>
    /// <remarks>Use this property to specify additional headers that should be sent with each request.
    /// Modifying the collection affects all subsequent requests made by this instance.</remarks>
    public HeaderTokens HeaderTokens { get; set; } = new HeaderTokens();

    /// <summary>
    ///     Header format.  Provide a string that specifies how the preamble of each message should be structured.  You can use
    ///     variables including:
    ///     {ts}: UTC timestamp
    ///     {host}: Hostname
    ///     {category}: Category
    ///     {thread}: Thread ID
    ///     {sev}: Severity
    ///     Default: {ts} {tz} {host} {category} {thread} {sev}
    ///     A space will be inserted between the header and the message.
    /// </summary>
    public string HeaderFormat
    {
        get => _headerFormat;
        set
        {
            if (string.IsNullOrEmpty(value))
            {
                _headerFormat = "";
            }
            else
            {
                _headerFormat = value;
            }
        }
    }

    public Func<HeaderContext, string> CustomHeaderFormatter { get; set; }

    public string EnvironmentName { get; set; }

    /// <summary>
    ///     Timestamp format.
    /// </summary>
    public string TimestampFormat
    {
        get => _timestampFormat;
        set
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentNullException(nameof(HeaderFormat));
            }

            _timestampFormat = value;
        }
    }

    /// <summary>
    ///     Enable or disable console logging.
    ///     Settings this to true will first validate if a console exists.
    ///     If a console is not available, it will be set to false.
    /// </summary>
    public bool EnableConsole
    {
        get => _enableConsole;
        set
        {
            if (value)
            {
                _enableConsole = ConsoleExists();
            }
            else
            {
                _enableConsole = false;
            }
        }
    }

    /// <summary>
    ///     Enable or disable use of color for console messages.
    /// </summary>
    public bool EnableColors { get; set; } = true;

    /// <summary>
    ///     Colors to use for console messages based on message severity.
    /// </summary>
    public ColorSchema Colors
    {
        get => _colors;
        set
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(Colors));
            }

            _colors = value;
        }
    }

    private readonly List<ELogType> _defaultLogTypes = new List<ELogType> { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE };
    private List<ELogType> _logTypes = new List<ELogType> { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE };
    public List<ELogType> TypesToLog
    {
        get
        {
            if (_logTypes == null)
            {
                _logTypes = _defaultLogTypes;
            }
            return _logTypes;
        }

        set
        {
            _logTypes = value;
        }
    }

    public List<Servers.Syslog.Syslog> SysLogServers { get; internal set; }
    public List<Servers.Splunk.Splunk> SplunkServers { get; internal set; }

    public List<Graylog> GrayLogServers { get; internal set; }
    public List<Servers.Tcp.Tcp> TcpServers { get; internal set; }

    public List<Servers.Udp.Udp> UdpServers { get; internal set; }

    /// <summary>
    ///     Determines if the fileLogging is enabled
    /// </summary>
    public bool EnableFileLogging { get; set; } = true;

    /// <summary>
    ///     FileLogger settings.
    /// </summary>
    public FileLoggerOptions FileLoggerOptions
    {
        get
        {
            if (_fileLoggerOptions == null)
            {
                _fileLoggerOptions = CreateDefaultFileLoggerOptions();
            }

            return _fileLoggerOptions;
        }

        set => _fileLoggerOptions = value;
    }

    /// <summary>
    ///     Set the origin of where the OnLog event was initiated
    /// </summary>
    public string LogOrigin { get; set; }

    /// <summary>
    /// Determines if we need to mask certain keywords
    /// </summary>
    public bool UseMask { get; set; }

    /// <summary>
    /// Determines the keywords to mask
    /// </summary>
    public List<string> MaskedKeywords { get; set; } = new List<string>();
    public string Mask { get; set; } = "***MASKED***";

    /// <summary>
    /// Determines that if masking is enabled we also need to use the default masking options:
    /// IP addresses
    /// MAC addresses
    /// Emails
    /// Passwords
    /// Credit card numbers
    /// Social security numbers (SSN) and BSN (Dutch Citizen Service Number)
    /// API keys/tokens
    /// Phone numbers (generic and Dutch specific)
    /// Dates of birth (DOB) or other date formats
    /// IBAN/Bank account numbers (generic and Dutch specific)
    /// JWT tokens
    /// URLs with sensitive query strings
    /// License keys
    /// Public and private keys (e.g., PEM format)
    /// Dutch KVK number (8 or 12 digits)
    /// Dutch BTW-nummer (VAT number)
    /// Dutch driving license number (10-12 characters)
    /// Dutch health insurance number (Zorgnummer)
    /// Other Dutch Bank Account numbers (9-10 digits)
    /// Dutch Passport Numbers (9 alphanumeric characters
    /// Dutch Identification Document Numbers (varying formats)
    /// Custom keywords specified in LoggerSettings
    /// </summary>
    public bool UseDefaultMasking { get; set; } = true;

    public event LogDelegate OnLog;

    private static FileLoggerOptions CreateDefaultFileLoggerOptions()
    {
        return new FileLoggerOptions();
    }


    private static bool ConsoleExists()
    {
        try
        {
            var test1 = Environment.UserInteractive;
            var test2 = Console.WindowHeight > 0;
            return test1 && test2;
        }
        catch (Exception)
        {
            return false;
        }
    }

    internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage)
    {
        OnLog?.Invoke(eonaCatLogMessage);
    }

    internal void ResetLogEvent()
    {
        OnLog = null;
    }

    public void AllowAllLogTypes()
    {
        TypesToLog.Clear();
    }

    public void LogInfo()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.INFO))
        {
            TypesToLog.Add(ELogType.INFO);
        }
    }

    public void LogWarning()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.WARNING))
        {
            TypesToLog.Add(ELogType.WARNING);
        }
    }

    public void LogError()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.ERROR))
        {
            TypesToLog.Add(ELogType.ERROR);
        }
    }

    public void LogCritical()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.CRITICAL))
        {
            TypesToLog.Add(ELogType.CRITICAL);
        }
    }

    public void LogTraffic()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.TRAFFIC))
        {
            TypesToLog.Add(ELogType.TRAFFIC);
        }
    }

    public void LogTrace()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.TRACE))
        {
            TypesToLog.Add(ELogType.TRACE);
        }
    }

    public void LogDebug()
    {
        if (TypesToLog == null)
        {
            TypesToLog = new List<ELogType>();
        }

        if (!TypesToLog.Contains(ELogType.DEBUG))
        {
            TypesToLog.Add(ELogType.DEBUG);
        }
    }
}