using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EonaCat.LogStack.Chaining;

// 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>
/// Defines the contract for chaining multiple logger instances together.
/// Allows logs to be propagated through a chain of loggers, enabling the ability
/// to change log libraries or add multiple logging backends together.
/// </summary>
public interface ILoggerChain : IAsyncDisposable
{
    /// <summary>
    /// Gets the name/identifier of this logger chain
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Gets the primary logger in the chain
    /// </summary>
    EonaCatLogStack Primary { get; }

    /// <summary>
    /// Gets the list of chained loggers that receive logs from the primary logger
    /// </summary>
    IReadOnlyList<EonaCatLogStack> ChainedLoggers { get; }

    /// <summary>
    /// Adds a logger to the chain. Logs will be propagated to this logger.
    /// </summary>
    /// <param name="logger">The logger to add to the chain</param>
    /// <param name="chainAsFlow">If true, the logger will be added as a flow to the primary logger</param>
    /// <returns>This instance for fluent chaining</returns>
    ILoggerChain Add(EonaCatLogStack logger, bool chainAsFlow = true);

    /// <summary>
    /// Adds multiple loggers to the chain.
    /// </summary>
    /// <param name="loggers">The loggers to add</param>
    /// <returns>This instance for fluent chaining</returns>
    ILoggerChain AddRange(params EonaCatLogStack[] loggers);

    /// <summary>
    /// Removes a logger from the chain.
    /// </summary>
    /// <param name="logger">The logger to remove</param>
    /// <returns>True if the logger was removed, false if it wasn't in the chain</returns>
    bool Remove(EonaCatLogStack logger);

    /// <summary>
    /// Removes all loggers from the chain except the primary.
    /// </summary>
    void Clear();

    /// <summary>
    /// Gets the behavior when a chained logger fails
    /// </summary>
    LoggerChainErrorBehavior ErrorBehavior { get; set; }

    /// <summary>
    /// Flushes all loggers in the chain.
    /// </summary>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>A task representing the asynchronous flush operation</returns>
    Task FlushAllAsync(CancellationToken cancellationToken = default);

    /// <summary>
    /// Disables a specific logger in the chain temporarily
    /// </summary>
    /// <param name="logger">The logger to disable</param>
    void DisableLogger(EonaCatLogStack logger);

    /// <summary>
    /// Enables a previously disabled logger in the chain
    /// </summary>
    /// <param name="logger">The logger to enable</param>
    void EnableLogger(EonaCatLogStack logger);

    /// <summary>
    /// Gets the enabled state of a logger in the chain
    /// </summary>
    /// <param name="logger">The logger to check</param>
    /// <returns>True if the logger is enabled, false otherwise</returns>
    bool IsLoggerEnabled(EonaCatLogStack logger);
}

/// <summary>
/// Specifies how the logger chain should behave when a chained logger encounters an error
/// </summary>
public enum LoggerChainErrorBehavior
{
    /// <summary>
    /// Continue processing the chain even if a logger fails
    /// </summary>
    ContinueOnError = 0,

    /// <summary>
    /// Stop the chain if any logger fails
    /// </summary>
    StopOnError = 1,

    /// <summary>
    /// Skip the failed logger and continue with the rest
    /// </summary>
    SkipFailed = 2
}

/// <summary>
/// Event arguments for logger chain events
/// </summary>
public sealed class LoggerChainEventArgs : EventArgs
{
    /// <summary>
    /// The logger involved in the event
    /// </summary>
    public EonaCatLogStack Logger { get; set; }

    /// <summary>
    /// The event message
    /// </summary>
    public string Message { get; set; }

    /// <summary>
    /// An optional exception associated with the event
    /// </summary>
    public Exception? Exception { get; set; }
}
