using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace EonaCat.Logger.EonaCatCoreLogger
{
    internal sealed class LoggerScopedContext
    {
        private static readonly AsyncLocal<Stack<Dictionary<string, string>>> _scopes
            = new();

        public IDisposable BeginScope<TState>(TState state)
        {
            _scopes.Value ??= new Stack<Dictionary<string, string>>();
            _scopes.Value.Push(new Dictionary<string, string>());
            return new ScopePopper();
        }

        public void Set(string key, string value)
        {
            if (_scopes.Value == null || _scopes.Value.Count == 0)
            {
                return;
            }

            _scopes.Value.Peek()[key] = value;
        }

        public string Get(string key)
        {
            if (_scopes.Value == null)
            {
                return null;
            }

            foreach (var scope in _scopes.Value)
            {
                if (scope.TryGetValue(key, out var value))
                {
                    return value;
                }
            }

            return null;
        }

        public IReadOnlyDictionary<string, string> GetAll()
        {
            var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            if (_scopes.Value == null)
            {
                return result;
            }

            // Iterate from top of stack to bottom
            foreach (var scope in _scopes.Value)
            {
                foreach (var kv in scope)
                {
                    if (!result.ContainsKey(kv.Key))
                    {
                        result[kv.Key] = kv.Value;
                    }
                }
            }

            return result;
        }


        public void Clear()
        {
            _scopes.Value?.Clear();
        }

        private sealed class ScopePopper : IDisposable
        {
            public void Dispose()
            {
                if (_scopes.Value?.Count > 0)
                {
                    _scopes.Value.Pop();
                }
            }
        }
    }
}
