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

namespace EonaCat.LogStack.Core;

// 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>
/// Thread-safe runtime log-level controller.
///
/// Attach an instance to a logger via <c>logger.UseDynamicLevel(controller)</c>.
/// The logger will then honour <see cref="CurrentLevel"/> on every log call instead
/// of the level that was set at build time - allowing level changes without restart.
///
/// <example>
/// <code>
/// var levelCtrl = new DynamicLevelController(LogLevel.Information);
/// var logger = new LogBuilder()
///     .WriteToConsole()
///     .Build()
///     .UseDynamicLevel(levelCtrl);
///
/// // Later, crank up verbosity for debugging:
/// levelCtrl.CurrentLevel = LogLevel.Trace;
///
/// // Or subscribe to changes:
/// levelCtrl.LevelChanged += (old, @new) => Console.WriteLine($"Level: {old} → {@new}");
/// </code>
/// </example>
/// </summary>
public sealed class DynamicLevelController
{
    // Store as int for Interlocked operations
    private int _level;

    /// <summary>Fires whenever the level changes, passing (oldLevel, newLevel).</summary>
    public event Action<LogLevel, LogLevel>? LevelChanged;

    public DynamicLevelController(LogLevel initialLevel = LogLevel.Information)
    {
        _level = (int)initialLevel;
    }

    /// <summary>Gets or sets the current minimum log level.</summary>
    public LogLevel CurrentLevel
    {
        get => (LogLevel)Volatile.Read(ref _level);
        set
        {
            var old = (LogLevel)Interlocked.Exchange(ref _level, (int)value);
            if (old != value)
            {
                LevelChanged?.Invoke(old, value);
            }
        }
    }

    /// <summary>
    /// Temporarily raises the level to <paramref name="level"/> and returns a handle
    /// that restores the previous level when disposed.
    /// Thread-safe for nested/concurrent usage.
    /// </summary>
    public IDisposable TemporaryLevel(LogLevel level)
    {
        var previous = CurrentLevel;
        CurrentLevel = level;
        return new RestoreHandle(this, previous);
    }

    private sealed class RestoreHandle : IDisposable
    {
        private readonly DynamicLevelController _ctrl;
        private readonly LogLevel _restore;
        private int _disposed;

        public RestoreHandle(DynamicLevelController ctrl, LogLevel restore)
        {
            _ctrl = ctrl;
            _restore = restore;
        }

        public void Dispose()
        {
            if (Interlocked.Exchange(ref _disposed, 1) == 0)
            {
                _ctrl.CurrentLevel = _restore;
            }
        }
    }
}
