using EonaCat.LogStack.Core;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace EonaCat.LogStack.Flows;

// 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>
/// Flows are output destinations for log events (replacement for "sinks").
/// Each flow handles writing log events to a specific destination with optimized batching.
/// </summary>
public interface IFlow : IAsyncDisposable
{
    /// <summary>
    /// Gets the name of this flow for identification
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Minimum log level this flow will process
    /// </summary>
    LogLevel MinimumLevel { get; }

    /// <summary>
    /// Whether this flow is currently enabled
    /// </summary>
    bool IsEnabled { get; }

    /// <summary>
    /// Blast a single log event to this flow
    /// </summary>
    Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken cancellationToken = default);

    /// <summary>
    /// Blast a batch of log events to this flow (more efficient than single blasts)
    /// </summary>
    Task<WriteResult> BlastBatchAsync(ReadOnlyMemory<LogEvent> logEvents, CancellationToken cancellationToken = default);

    /// <summary>
    /// Flush any buffered log events immediately
    /// </summary>
    Task FlushAsync(CancellationToken cancellationToken = default);
}

/// <summary>
/// Base class for flows with common functionality
/// </summary>
public abstract class FlowBase : IFlow
{
    protected FlowBase(string name, LogLevel minimumLevel = LogLevel.Trace)
    {
        Name = name ?? throw new ArgumentNullException(nameof(name));
        MinimumLevel = minimumLevel;
        IsEnabled = true;
    }

    public string Name { get; }
    public LogLevel MinimumLevel { get; protected set; }
    public bool IsEnabled { get; protected set; }

    protected long DroppedCount;
    protected long BlastedCount;

    protected bool IsLogLevelEnabled(LogEvent logEvent)
    {
        return logEvent.Level >= MinimumLevel;
    }

    public abstract Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken cancellationToken = default);

    public virtual async Task<WriteResult> BlastBatchAsync(ReadOnlyMemory<LogEvent> logEvents, CancellationToken cancellationToken = default)
    {
        var result = WriteResult.Success;
        var eventsArray = logEvents.ToArray();

        foreach (var logEvent in eventsArray)
        {
            var singleResult = await BlastAsync(logEvent, cancellationToken).ConfigureAwait(false);
            if (singleResult != WriteResult.Success)
            {
                result = singleResult;
            }
        }

        return result;
    }

    public abstract Task FlushAsync(CancellationToken cancellationToken = default);

    public virtual async ValueTask DisposeAsync()
    {
        IsEnabled = false;
        await FlushAsync(default).ConfigureAwait(false);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Gets diagnostic information about this flow
    /// </summary>
    public virtual FlowDiagnostics GetDiagnostics()
    {
        return new FlowDiagnostics
        {
            Name = Name,
            IsEnabled = IsEnabled,
            MinimumLevel = MinimumLevel,
            BlastedCount = Interlocked.Read(ref BlastedCount),
            DroppedCount = Interlocked.Read(ref DroppedCount)
        };
    }
}

/// <summary>
/// Diagnostic information about a flow
/// </summary>
public sealed class FlowDiagnostics
{
    public string Name { get; set; }
    public bool IsEnabled { get; set; }
    public LogLevel MinimumLevel { get; set; }
    public long BlastedCount { get; set; }
    public long DroppedCount { get; set; }
}