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

namespace EonaCat.Logger.EonaCatCoreLogger
{
    // 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.

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

        private void EnsureScope()
        {
            _scopes.Value ??= new Stack<Dictionary<string, string>>();
            if (_scopes.Value.Count == 0)
            {
                _scopes.Value.Push(new Dictionary<string, string>());
            }
        }

        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)
        {
            EnsureScope();
            _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()
        {
            EnsureScope();

            var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            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();
                }
            }
        }
    }
}
