using EonaCat.LogStack.Boosters;
using EonaCat.LogStack.Core;
using EonaCat.LogStack.EonaCatLogStackCore;
using EonaCat.LogStack.EonaCatLogStackCore.Policies;
using EonaCat.LogStack.Flows;
using ServiceMonitoring;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace EonaCat.LogStack.Configuration;

// 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>
/// Fluent builder for configuring the logger with flows and boosters
/// </summary>
public sealed class LogBuilder
{
    private readonly string _category;
    private LogLevel _minimumLevel = LogLevel.Trace;
    private TimestampMode _timestampMode = TimestampMode.Utc;
    private readonly List<IFlow> _flows = new();
    private readonly List<IBooster> _boosters = new();
    private DynamicLevelController? _dynamicLevel;
    private bool _useAsyncPipeline;
    private int _asyncPipelineCapacity = 65536;

    public event EventHandler<LogMessage> OnLog;

    public LogBuilder(string category = "Application")
    {
        _category = category ?? throw new ArgumentNullException(nameof(category));
        UseAsyncPipeline();
    }

    /// <summary>
    /// Sets the minimum log level
    /// </summary>
    public LogBuilder WithMinimumLevel(LogLevel level)
    {
        _minimumLevel = level;
        return this;
    }

    /// <summary>
    /// Sets the timestamp mode
    /// </summary>
    public LogBuilder WithTimestampMode(TimestampMode mode)
    {
        _timestampMode = mode;
        return this;
    }

    /// <summary>
    /// Adds console output
    /// </summary>
    public LogBuilder WriteToConsole(
        LogLevel minimumLevel = LogLevel.Trace,
        bool useColors = true)
    {
        _flows.Add(new ConsoleFlow(minimumLevel, useColors, _timestampMode));
        return this;
    }

    /// <summary>
    /// Adds diagnostics
    /// </summary>
    public LogBuilder WriteDiagnostics(
        TimeSpan snapshotInterval = default(TimeSpan),
            bool injectIntoEvents = false,
            bool writeSnapshotEvents = true,
            string snapshotCategory = "Diagnostics",
            IFlow forwardTo = null,
            LogLevel minimumLevel = LogLevel.Trace,
            Func<Dictionary<string, object>> customMetrics = null)
    {
        _flows.Add(new DiagnosticsFlow(
            snapshotInterval,
            injectIntoEvents,
            writeSnapshotEvents,
            snapshotCategory,
            forwardTo,
            minimumLevel,
            customMetrics));
        return this;
    }

    /// <summary>
    /// Adds file output
    /// </summary>
    public LogBuilder WriteToFile(
        string directory,
        string filePrefix = "log",
        long maxFileSize = 100 * 1024 * 1024,
        long maxDirectorySize = 2L * 1024 * 1024 * 1024,
        FileRetentionPolicy fileRetentionPolicy = null,
        int flushIntervalInMilliSeconds = 2000,
        bool useCategoryRouting = false,
        int batchSize = 1,
        LogLevel[]? logLevelsForSeparateFiles = null,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait,
        FileOutputFormat outputFormat = FileOutputFormat.Text,
        CompressionFormat compression = CompressionFormat.GZip,
        string template = "[{ts}] [Host: {host}] [Category: {category}] [Thread: {thread}] [{logtype}] {message}{props}")
    {
        _flows.Add(new FileFlow(
            directory,
            filePrefix,
            maxFileSize,
            maxDirectorySize,
            fileRetentionPolicy,
            flushIntervalInMilliSeconds,
            batchSize,
            minimumLevel,
            useCategoryRouting,
            logLevelsForSeparateFiles,
            _timestampMode,
            backpressureStrategy,
            outputFormat,
            compression,
            template));
        return this;
    }

    public LogBuilder WriteToEncryptedFile(
        string directory,
        string filePrefix = "log",
        string password = "EonaCat",
        long maxFileSize = 100 * 1024 * 1024,
        FileRetentionPolicy fileRetentionPolicy = null,
        int flushIntervalInMilliSeconds = 2000,
        bool useCategoryRouting = false,
        LogLevel[]? logLevelsForSeparateFiles = null,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new EncryptedFileFlow(
            directory,
            password,
            filePrefix,
            maxFileSize,
            maxDirectorySize: 2L * 1024 * 1024 * 1024,
            retention: fileRetentionPolicy,
            flushIntervalMs: flushIntervalInMilliSeconds,
            batchSize: 1,
            minimumLevel: minimumLevel,
            useCategoryRouting: useCategoryRouting,
            logLevelsForSeparateFiles: logLevelsForSeparateFiles,
            tsMode: _timestampMode));
        return this;
    }

    /// <summary>
    /// Write to a rolling buffer
    /// </summary>
    /// <param name="capacity">Maximum number of events to retain.</param>
    /// <param name="minimumLevel">Minimum level to store in the buffer.</param>
    /// <param name="triggerLevel">
    ///   When a log event reaches this level or above, the current buffer
    ///   contents are immediately forwarded to <paramref name="triggerTarget"/>.
    ///   Set to <c>LogLevel.None</c> (or omit) to disable.
    /// </param>
    /// <param name="triggerTarget">
    ///   Flow to forward the buffered context to when the trigger fires.
    ///   Can be null even when <paramref name="triggerLevel"/> is set.
    /// </param>
    /// <param name="preContextLines">
    ///   How many buffered lines to forward before the triggering event.
    ///   Defaults to entire buffer (int.MaxValue).
    /// </param>
    /// <returns></returns>
    public LogBuilder WriteToRollingBuffer(
        int capacity = 500,
        LogLevel minimumLevel = LogLevel.Trace,
        LogLevel triggerLevel = LogLevel.Error,
        IFlow triggerTarget = null,
        int preContextLines = int.MaxValue)
    {
        _flows.Add(new RollingBufferFlow(
            capacity,
            minimumLevel,
            triggerLevel,
            triggerTarget,
            preContextLines));
        return this;
    }

    /// <summary>
    /// Decrypt a file which is encrypted by EonaCat Logger
    /// </summary>
    /// <param name="encryptedPath">encrypted file source path</param>
    /// <param name="outputPath">destination path for decrypted file</param>
    /// <param name="password">password used by encryption</param>
    /// <returns></returns>
    public static bool DecryptFile(string encryptedPath, string outputPath, string password)
    {
        return EncryptedFileFlow.DecryptToFile(encryptedPath, outputPath, password);
    }

    /// <summary>
    /// Adds Database output
    /// </summary>
    public LogBuilder WriteToDatabase(
        Func<DbConnection> connectionFactory,
        string tableName = "logs",
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new DatabaseFlow(
            connectionFactory,
            tableName,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Snmp traps
    /// </summary>
    public LogBuilder WriteToSnmpTrap(string host, int port = 162, string oid = "1.3.6.1.4.1.9999", LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new SnmpTrapFlow(
            host,
            port,
            oid,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Discord
    /// </summary>
    public LogBuilder WriteToDiscord(
        string webHookUrl,
        string botName = "EonaCatBot",
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new DiscordFlow(
            webHookUrl,
            botName,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds ElasticSearch
    /// </summary>
    public LogBuilder WriteToElasticSearch(
        string elasticSearchUrl,
        string indexName = "EonaCatIndex",
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new ElasticSearchFlow(
            elasticSearchUrl,
            indexName,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Telegram
    /// </summary>
    public LogBuilder WriteToTelegram(string botToken, string chatId = "EonaCat", int batchSize = 1, LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new TelegramFlow(
            botToken,
            chatId,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds a tamper-evident audit trail.
    ///
    /// Each audit entry is hash-chained: every line stores a SHA-256 of the previous
    /// line's hash + the current entry body, so deletion or modification of any past
    /// entry invalidates all subsequent hashes.
    ///
    /// Use <see cref="AuditFlow.Verify(string)"/> to verify file integrity at any time.
    /// </summary>
    /// <param name="directory">Directory where the .audit file is written.</param>
    /// <param name="filePrefix">File name prefix (default: "audit").</param>
    /// <param name="auditLevel">
    ///   Which severity levels are recorded in the audit trail:
    ///   <list type="bullet">
    ///     <item><see cref="AuditLevel.All"/> – every log event (default)</item>
    ///     <item><see cref="AuditLevel.WarningAndAbove"/> – Warning, Error, Critical</item>
    ///     <item><see cref="AuditLevel.ErrorAndAbove"/> – Error and Critical only</item>
    ///     <item><see cref="AuditLevel.CriticalOnly"/> – Critical only</item>
    ///   </list>
    /// </param>
    /// <param name="minimumLevel">Minimum <see cref="LogLevel"/> for audit capture.</param>
    /// <param name="includeProperties">Whether structured properties are appended to each entry.</param>
    public LogBuilder WriteToAudit(
        string directory,
        string filePrefix = "audit",
        AuditLevel auditLevel = AuditLevel.All,
        LogLevel minimumLevel = LogLevel.Trace,
        bool includeProperties = true)
    {
        _flows.Add(new AuditFlow(
            directory,
            filePrefix,
            auditLevel,
            minimumLevel,
            includeProperties));
        return this;
    }

    /// <summary>
    /// Adds Slack
    /// </summary>
    public LogBuilder WriteToSlack(string webhookUrl, int batchSize = 1, LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new SlackFlow(
            webhookUrl,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Slack
    /// </summary>
    public LogBuilder WriteToMicrosoftTeams(string webhookUrl, int batchSize = 1, LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new MicrosoftTeamsFlow(
            webhookUrl,
            batchSize,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds a TCP flow.
    /// </summary>
    public LogBuilder WriteToTcp(
        string host,
        int port,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait,
        bool useTls = false,
        RemoteCertificateValidationCallback certValidationCallback = null,
        X509CertificateCollection clientCertificates = null)
    {
        if (string.IsNullOrWhiteSpace(host))
        {
            throw new ArgumentException("Host cannot be null or empty.", nameof(host));
        }

        if (port <= 0 || port > 65535)
        {
            throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 1 and 65535.");
        }

        _flows.Add(new TcpFlow(
            host,
            port,
            batchSize,
            minimumLevel,
            backpressureStrategy,
            useTls,
            certValidationCallback,
            clientCertificates));

        return this;
    }



    /// <summary>
    /// Adds a Retry flow to retry failed log writes.
    /// </summary>
    public LogBuilder WriteToRetry(
        IFlow primaryFlow,
        int maxRetries = 3,
        TimeSpan? initialDelay = null,
        bool exponentialBackoff = true)
    {
        if (primaryFlow == null)
        {
            throw new ArgumentNullException(nameof(primaryFlow));
        }

        _flows.Add(new RetryFlow(
            primaryFlow,
            maxRetries,
            initialDelay ?? TimeSpan.FromMilliseconds(200),
            exponentialBackoff));

        return this;
    }

    /// <summary>
    /// Adds an EventLog flow for sending logs to a remote destination.
    /// </summary>
    public LogBuilder WriteToEventLogFlow(
        string destination,
        int port = 514,
        LogLevel minimumLevel = LogLevel.Trace,
        int bufferSize = 100,
        TimeSpan? flushInterval = null,
        bool useTls = false,
        RemoteCertificateValidationCallback? certificateValidationCallback = null,
        X509CertificateCollection? clientCertificates = null)
    {
        if (string.IsNullOrWhiteSpace(destination))
        {
            throw new ArgumentException("Destination cannot be null or empty.", nameof(destination));
        }

        _flows.Add(new EventLogFlow(
            destination,
            port,
            minimumLevel,
            bufferSize,
            flushInterval ?? TimeSpan.FromSeconds(5),
            useTls,
            certificateValidationCallback,
            clientCertificates));
        return this;
    }

    /// <summary>
    /// Adds a Failover flow that switches to a secondary flow if the primary fails.
    /// </summary>
    public LogBuilder WriteToFailover(IFlow primaryFlow, IFlow secondaryFlow, TimeSpan? recoveryCheckInterval = null, int failureThreshold = 5)
    {
        if (primaryFlow == null)
        {
            throw new ArgumentNullException(nameof(primaryFlow));
        }

        if (secondaryFlow == null)
        {
            throw new ArgumentNullException(nameof(secondaryFlow));
        }

        _flows.Add(new FailoverFlow(primaryFlow, secondaryFlow, recoveryCheckInterval, failureThreshold));

        return this;
    }

    /// <summary>
    /// Adds a Syslog TCP flow.
    /// </summary>
    public LogBuilder WriteToSyslogTcp(
        string host,
        int port = 514,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait,
        bool useTls = false,
        RemoteCertificateValidationCallback certValidationCallback = null,
        X509CertificateCollection clientCertificates = null)
    {
        if (string.IsNullOrWhiteSpace(host))
        {
            throw new ArgumentException("Host cannot be null or empty.", nameof(host));
        }

        if (port <= 0 || port > 65535)
        {
            throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 1 and 65535.");
        }

        _flows.Add(new SyslogTcpFlow(
            host,
            port,
            batchSize,
            minimumLevel,
            backpressureStrategy,
            useTls,
            certValidationCallback,
            clientCertificates));

        return this;
    }

    /// <summary>
    /// Adds a Status Monitoring flow.
    /// </summary>
    public LogBuilder WriteToStatusFlow(
        List<ServiceStatus> servicesToMonitor,
        TimeSpan? checkInterval = null,
        string statusDirectory = null,
        Action<ServiceStatus> statusChangedTrigger = null)
    {
        _flows.Add(new StatusFlow(
            servicesToMonitor,
            checkInterval,
            statusDirectory,
            statusChangedTrigger));

        return this;
    }

    /// <summary>
    /// Adds Syslog Tcp
    /// </summary>
    public LogBuilder WriteToSyslogUdp(
        string host,
        int port,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait)
    {
        _flows.Add(new SyslogUdpFlow(
            host,
            port,
            batchSize,
            minimumLevel,
            backpressureStrategy));
        return this;
    }

    /// <summary>
    /// Adds Zabbix
    /// </summary>
    public LogBuilder WriteToZabbixFlow(
        string host,
        int port = 10051,
        string zabbixHostname = null,
        string zabbixKey = "log_event",
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait)
    {
        _flows.Add(new ZabbixFlow(
            host,
            port,
            zabbixHostname,
            zabbixKey,
            batchSize,
            minimumLevel,
            backpressureStrategy));
        return this;
    }

    /// <summary>
    /// Adds Graylog
    /// </summary>
    public LogBuilder WriteToGraylogFlow(
        string host,
        int port = 12201,
        bool useTcp = false,
        string graylogHostName = null,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait)
    {
        _flows.Add(new GraylogFlow(
            host,
            port,
            useTcp,
            graylogHostName,
            batchSize,
            minimumLevel,
            backpressureStrategy));
        return this;
    }

    /// <summary>
    /// Publishes log events to a Redis channel using the PUBLISH command (Pub/Sub)
    /// and optionally appends them to a Redis List (LPUSH) for persistence.
    ///
    /// Uses raw TCP + RESP protocol, so there arent additional dependencies
    ///
    /// Features:
    ///  - Reconnect with exponential back-off on connection failure
    ///  - Optional LPUSH to a list key with LTRIM to cap list length
    ///  - Optional password authentication (AUTH command)
    ///  - Optional DB selection (SELECT command)
    ///  - Background writer thread (non-blocking callers)
    /// </summary>
    public LogBuilder RedisFlow(string host = "localhost",
            int port = 6379,
            string password = null,
            int database = 0,
            string channel = "eonacat:logs",
            string listKey = null,
            int maxListLength = 10000,
            LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new RedisFlow(
            host,
            port,
            password,
            database,
            channel,
            listKey,
            maxListLength,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Wraps any flow with token-bucket rate limiting and optional message deduplication.
    /// Ideal for protecting high-latency sinks (email, Slack, HTTP) from log storms.
    /// </summary>
    /// <param name="inner">The downstream flow to protect.</param>
    /// <param name="burstCapacity">
    ///   Max events that can be emitted in a burst per level (token bucket capacity).
    /// </param>
    /// <param name="refillPerSecond">
    ///   How many tokens are added per second per level. E.g. 5.0 = 5 events/second steady state.
    /// </param>
    /// <param name="deduplicate">
    ///   If true, identical messages within <paramref name="dedupWindow"/> are collapsed.
    ///   The suppressed count is appended to the message when the window expires.
    /// </param>
    /// <param name="dedupWindow">Deduplication window (default 60 s).</param>
    /// <param name="dedupMaxKeys">Maximum number of distinct messages tracked (default 1000).</param>
    /// <param name="minimumLevel">Minimum level this flow processes.</param>
    public LogBuilder WriteToThrottled(IFlow inner,
        int burstCapacity = 10,
        double refillPerSecond = 1.0,
        bool deduplicate = false,
        TimeSpan dedupWindow = default(TimeSpan),
        int dedupMaxKeys = 1000,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new ThrottledFlow(
            inner,
            burstCapacity,
            refillPerSecond,
            deduplicate,
            dedupWindow,
            dedupMaxKeys, 
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Splunk
    /// </summary>
    public LogBuilder WriteToSplunkFlow(
        string splunkUrl,
        string token,
        string sourceType = "splunk_logs",
        string hostName = null,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait)
    {
        _flows.Add(new SplunkFlow(
            splunkUrl,
            token,
            sourceType,
            hostName,
            batchSize,
            minimumLevel,
            backpressureStrategy));
        return this;
    }

    /// <summary>
    /// Pushes log events to a SignalR hub via HTTP POST to the hub's /send endpoint.
    /// Works with ASP.NET SignalR (classic) and ASP.NET Core SignalR server-side REST API.
    ///
    /// A lightweight alternative to the SignalR client library
    ///
    /// On the server side you need a minimal hub endpoint that accepts POST:
    ///   POST {hubUrl}/send  body: { "target": "...", "arguments": [ { log json } ] }
    ///
    /// For live dashboards: the hub broadcasts to a "logs" group; clients subscribe and
    /// render events in real time.
    /// </summary>
    public LogBuilder WriteToSignalR(
            string hubUrl,
            string hubMethod = "ReceiveLog",
            HttpClient httpClient = null,
            int batchSize = 20,
            int batchIntervalMs = 500,
            LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new SignalRFlow(
            hubUrl,
            hubMethod,
            httpClient,
            batchSize,
            batchIntervalMs,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Sends log events as HTML email digests via SMTP.
    /// Batches events for <paramref name="digestMinutes"/> before sending,
    /// unless flushOnCritical is true (Critical events bypass batching).
    /// </summary>
    public LogBuilder WriteToEmail(
        string smtpHost,
            int smtpPort = 587,
            bool useSsl = true,
            string username = null,
            string password = null,
            string from = null,
            string to = null,
            string subjectPrefix = "[EonaCatLogStack]",
            int digestMinutes = 5,
            bool flushOnCritical = true,
            int maxEventsPerDigest = 100,
            string headerName = null,
            LogLevel minimumLevel = LogLevel.Error)
    {
        _flows.Add(new EmailFlow(
            smtpHost,
            smtpPort,
            useSsl,
            username,
            password,
            from,
            to,
            subjectPrefix,
            digestMinutes,
            flushOnCritical,
            maxEventsPerDigest,
            headerName,
            minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds Udp
    /// </summary>
    public LogBuilder WriteToUdp(
        string host,
        int port,
        int flushIntervalInMilliseconds = 1000,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        BackpressureStrategy backpressureStrategy = BackpressureStrategy.Wait)
    {
        _flows.Add(new UdpFlow(
            host,
            port,
            flushIntervalInMilliseconds,
            batchSize,
            minimumLevel,
            backpressureStrategy));
        return this;
    }

    /// <summary>
    /// Adds in-memory buffer output
    /// </summary>
    public LogBuilder WriteToMemory(
        int capacity = 10000,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new MemoryFlow(capacity, minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds HTTP endpoint output
    /// </summary>
    public LogBuilder WriteToHttp(
        string endpoint,
        HttpClient? httpClient = null,
        int batchSize = 1,
        LogLevel minimumLevel = LogLevel.Trace,
        TimeSpan? batchInterval = null,
        Dictionary<string, string>? headers = null)
    {
        _flows.Add(new HttpFlow(
            endpoint,
            httpClient,
            batchSize,
            minimumLevel,
            batchInterval,
            headers));
        return this;
    }

    /// <summary>
    /// Adds a custom flow
    /// </summary>
    public LogBuilder WriteTo(IFlow flow)
    {
        _flows.Add(flow ?? throw new ArgumentNullException(nameof(flow)));
        return this;
    }

    /// <summary>
    /// Boost logs with machine name
    /// </summary>
    public LogBuilder BoostWithMachineName()
    {
        _boosters.Add(new MachineNameBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with process ID
    /// </summary>
    public LogBuilder BoostWithProcessId()
    {
        _boosters.Add(new ProcessIdBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current date (yyyy-MM-dd)
    /// </summary>
    public LogBuilder BoostWithDate()
    {
        _boosters.Add(new DateBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current time (HH:mm:ss.fff)
    /// </summary>
    public LogBuilder BoostWithTime()
    {
        _boosters.Add(new TimeBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current timestamp ticks
    /// </summary>
    public LogBuilder BoostWithTicks()
    {
        _boosters.Add(new TicksBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the process start time
    /// </summary>
    public LogBuilder BoostWithProcStart()
    {
        _boosters.Add(new ProcStartBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the uptime of the process in seconds
    /// </summary>
    public LogBuilder BoostWithUptime()
    {
        _boosters.Add(new UptimeBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current thread name
    /// </summary>
    public LogBuilder BoostWithThreadName()
    {
        _boosters.Add(new ThreadNameBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with memory usage in MB
    /// </summary>
    public LogBuilder BoostWithMemory()
    {
        _boosters.Add(new MemoryBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the operating system description
    /// </summary>
    public LogBuilder BoostWithOS()
    {
        _boosters.Add(new OSBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the runtime/framework description
    /// </summary>
    public LogBuilder BoostWithFramework()
    {
        _boosters.Add(new FrameworkBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the application name and base directory
    /// </summary>
    public LogBuilder BoostWithApp()
    {
        _boosters.Add(new AppBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current user name
    /// </summary>
    public LogBuilder BoostWithUser()
    {
        _boosters.Add(new UserBooster());
        return this;
    }

    /// <summary>
    /// Boost logs with the current thread ID
    /// </summary>
    public LogBuilder BoostWithThreadId()
    {
        _boosters.Add(new ThreadIdBooster());
        return this;
    }


    /// <summary>
    /// Boost logs with custom text
    /// </summary>
    public LogBuilder BoostWithCustomText(string key, string value)
    {
        _boosters.Add(new CustomTextBooster(key, value));
        return this;
    }

    /// <summary>
    /// Boost logs with environment name
    /// </summary>
    public LogBuilder BoostWithEnvironment(string environmentName)
    {
        _boosters.Add(new EnvironmentBooster(environmentName));
        return this;
    }

    /// <summary>
    /// Boost logs with application name and version
    /// </summary>
    public LogBuilder BoostWithApplication(string applicationName, string? version = null)
    {
        _boosters.Add(new ApplicationBooster(applicationName, version));
        return this;
    }

    /// <summary>
    /// Boost logs with correlation ID from Activity
    /// </summary>
    public LogBuilder BoostWithCorrelationId()
    {
        _boosters.Add(new CorrelationIdBooster());
        return this;
    }

    /// <summary>
    /// Adds a custom booster
    /// </summary>
    public LogBuilder Boost(IBooster booster)
    {
        _boosters.Add(booster ?? throw new ArgumentNullException(nameof(booster)));
        return this;
    }

    /// <summary>
    /// Adds a callback-based booster
    /// </summary>
    public LogBuilder Boost(string name, Func<Dictionary<string, object?>> callback)
    {
        _boosters.Add(new CallbackBooster(name, callback));
        return this;
    }

    /// <summary>
    /// Builds the configured logger
    /// </summary>
    public EonaCatLogStack Build()
    {
        var logger = new EonaCatLogStack(_category, _minimumLevel, _timestampMode);
        logger.OnLog += (sender, message) => OnLog?.Invoke(sender, message);

        foreach (var flow in _flows)
        {
            logger.AddFlow(flow);
        }

        foreach (var booster in _boosters)
        {
            logger.AddBooster(booster);
        }

        if (_dynamicLevel != null)
        {
            logger.UseDynamicLevel(_dynamicLevel);
        }

        if (_useAsyncPipeline)
        {
            logger.UseAsyncPipeline(_asyncPipelineCapacity);
        }

        return logger;
    }

    /// <summary>
    /// Creates a default logger with console and file output
    /// </summary>
    public static EonaCatLogStack CreateDefault(
        string category = "Application",
        string? logDirectory = null)
    {
        var directory = logDirectory ?? Path.Combine(AppContext.BaseDirectory, "logs");

        return new LogBuilder(category)
            .WithMinimumLevel(LogLevel.Information)
            .WriteToConsole()
            .WriteToFile(directory)
            .BoostWithMachineName()
            .BoostWithProcessId()
            .Build();
    }

    /// <summary>
    /// Get a flow by name
    /// </summary>
    /// <param name="name"></param>
    public IFlow GetFlow(string name)
    {
        lock (_flows)
        {
            var flow = _flows.Find(x => x.Name == name);
            return flow;
        }
    }

    /// <summary>
    /// Get a flow by type
    /// </summary>
    /// <param name="type"></param>
    public IFlow GetFlow(Type type)
    {
        lock (_flows)
        {
            var flow = _flows.Find(x => x.GetType() == type);
            return flow;
        }
    }

    /// <summary>
    /// Add a flow to the logBuilder
    /// </summary>
    /// <param name="flow"></param>
    public void AddFlow(IFlow flow)
    {
        lock (_flows)
        {
            _flows.Add(flow);
        }
    }

    /// <summary>
    /// Removes a flow from the logBuilder
    /// </summary>
    /// <param name="flow">To be removed flow</param>
    public void RemoveFlow(IFlow flow)
    {
        lock (_flows)
        {
            _flows.Remove(flow);
        }
    }

    /// <summary>
    /// Removes a flow from the logBuilder by name
    /// </summary>
    /// <param name="name">To be removed flow name</param>
    public void RemoveFlow(string name)
    {
        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        if (string.IsNullOrWhiteSpace(name))
        {
            return;
        }

        lock (_flows) { _flows.RemoveAll(f => f.Name == name); }
    }

    /// <summary>
    /// Enables the Channel-based async dispatch pipeline for zero-blocking logging.
    /// Events are enqueued to a <see cref="System.Threading.Channels.Channel{T}"/> and
    /// consumed by a dedicated background Task.
    /// </summary>
    /// <param name="capacity">Bounded capacity (0 = unbounded).</param>
    public LogBuilder UseAsyncPipeline(int capacity = 65536)
    {
        _useAsyncPipeline = true;
        _asyncPipelineCapacity = capacity;
        return this;
    }

    /// <summary>
    /// Disables the async dispatch pipeline and uses a synchronous flow instead.
    /// </summary>
    /// <param name="capacity"></param>
    /// <returns></returns>
    public LogBuilder UseSyncPipeline(int capacity = 65536)
    {
        _useAsyncPipeline = false;
        _asyncPipelineCapacity = capacity;
        return this;
    }

    /// <summary>
    /// Attaches a <see cref="DynamicLevelController"/> so the minimum log level can
    /// be changed at runtime without restarting the application.
    /// </summary>
    public LogBuilder WithDynamicLevelController(DynamicLevelController controller)
    {
        _dynamicLevel = controller ?? throw new ArgumentNullException(nameof(controller));
        return this;
    }

    /// <summary>
    /// Wraps any flow with a Circuit Breaker that opens after repeated failures
    /// and probes for recovery after a configurable timeout.
    /// </summary>
    public LogBuilder WriteToCircuitBreaker(
        IFlow inner,
        int failureThreshold = 5,
        TimeSpan? recoveryTimeout = null,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        if (inner == null)
        {
            throw new ArgumentNullException(nameof(inner));
        }

        _flows.Add(new CircuitBreakerFlow(inner, failureThreshold, recoveryTimeout, minimumLevel));
        return this;
    }

    /// <summary>
    /// Wraps any flow with a predicate - events are forwarded only when the predicate returns true.
    /// </summary>
    public LogBuilder WriteToConditional(
        IFlow inner,
        Func<LogEvent, bool> predicate,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        if (inner     == null)
        {
            throw new ArgumentNullException(nameof(inner));
        }

        if (predicate == null)
        {
            throw new ArgumentNullException(nameof(predicate));
        }

        _flows.Add(new ConditionalFlow(inner, predicate, minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds a <see cref="MulticastFlow"/> that fans events out to multiple inner flows in parallel.
    /// Use <see cref="MulticastFlow.Add"/> to attach targets.
    /// </summary>
    public LogBuilder WriteToMulticast(
        MulticastFlow multicast)
    {
        if (multicast == null)
        {
            throw new ArgumentNullException(nameof(multicast));
        }

        _flows.Add(multicast);
        return this;
    }

    /// <summary>
    /// Pushes log events to a Grafana Loki instance.
    /// </summary>
    public LogBuilder WriteToLoki(
        string lokiUrl,
        Dictionary<string, string>? labels = null,
        int batchSize = 50,
        int batchIntervalMs = 1000,
        string? bearerToken = null,
        string? basicUser = null,
        string? basicPassword = null,
        HttpClient? httpClient = null,
        LogLevel minimumLevel = LogLevel.Trace)
    {
        _flows.Add(new LokiFlow(
            lokiUrl, labels, batchSize, batchIntervalMs,
            bearerToken, basicUser, basicPassword, httpClient, minimumLevel));
        return this;
    }

    /// <summary>
    /// Adds the <see cref="CallerInfoBooster"/> which captures caller member/file/line.
    /// </summary>
    public LogBuilder BoostWithCallerInfo()
    {
        _boosters.Add(new CallerInfoBooster());
        return this;
    }


    /// <summary>Adds a stable event fingerprint for grouping failures.</summary>
    public LogBuilder BoostWithExceptionFingerprint()
    {
        _boosters.Add(new ExceptionFingerprintBooster());
        return this;
    }

    /// <summary>Adds runtime health information to every event.</summary>
    public LogBuilder BoostWithHealthSnapshot()
    {
        _boosters.Add(new HealthSnapshotBooster());
        return this;
    }

    /// <summary>Adds a schema version field to every log event.</summary>
    public LogBuilder BoostWithSchemaVersion(string version = "1.0")
    {
        _boosters.Add(new SchemaVersionBooster(version));
        return this;
    }

    /// <summary>Adds a monotonic sequence number to every event.</summary>
    public LogBuilder BoostWithSequence()
    {
        _boosters.Add(new SequenceBooster());
        return this;
    }

    /// <summary>
    /// Creates a dependency-free compatibility facade exposing adapters for
    /// Serilog, log4net and NLog style integrations.
    /// </summary>
    public Compatibility.LoggingCompatibilityFacade AsCompatibility()
    {
        return new Compatibility.LoggingCompatibilityFacade(Build());
    }

    /// <summary>
    /// Creates a Serilog compatible adapter without adding Serilog dependency.
    /// </summary>
    public Compatibility.EonaCatSerilogAdapter AsSerilog()
    {
        return new Compatibility.EonaCatSerilogAdapter(Build());
    }

    /// <summary>
    /// Creates a log4net compatible adapter without adding log4net dependency.
    /// </summary>
    public Compatibility.EonaCatLog4NetAdapter AsLog4Net()
    {
        return new Compatibility.EonaCatLog4NetAdapter(Build());
    }

    /// <summary>
    /// Creates an NLog compatible adapter without adding NLog dependency.
    /// </summary>
    public Compatibility.EonaCatNLogAdapter AsNLog()
    {
        return new Compatibility.EonaCatNLogAdapter(Build());
    }

}
