using System;
using System.Collections.Generic;

namespace EonaCat.LogStack.Tracing
{
    /// <summary>
    /// Factory for creating and managing TracingSpans with automatic context management.
    /// Supports parent-child relationships and automatic scope management.
    /// </summary>
    public sealed class SpanFactory
    {
        private readonly List<TracingSpan> _activeSpans = new();
        private readonly object _activeLock = new();

        /// <summary>
        /// Creates a new span with auto-context inheritance
        /// </summary>
        public TracingSpan CreateSpan(string name, string kind = "internal")
        {
            var context = TraceContextManager.Current;
            var span = new TracingSpan(name, context) { Kind = kind };

            lock (_activeLock)
            {
                _activeSpans.Add(span);
            }

            return span;
        }

        /// <summary>
        /// Creates a span and returns a scope that manages its lifetime
        /// </summary>
        public IDisposable CreateSpanScope(string name, string kind = "internal")
        {
            var span = CreateSpan(name, kind);
            return new SpanScope(span, this);
        }

        /// <summary>
        /// Creates a span with a child trace context
        /// </summary>
        public TracingSpan CreateChildSpan(string name, string kind = "internal")
        {
            using (TraceContextManager.CreateChildScope())
            {
                return CreateSpan(name, kind);
            }
        }

        /// <summary>
        /// Gets all active spans
        /// </summary>
        public IReadOnlyList<TracingSpan> GetActiveSpans()
        {
            lock (_activeLock)
            {
                return _activeSpans.AsReadOnly();
            }
        }

        /// <summary>
        /// Executes action within a span
        /// </summary>
        public void ExecuteInSpan(string name, Action action, string kind = "internal")
        {
            using (CreateSpanScope(name, kind))
            {
                action();
            }
        }

        /// <summary>
        /// Executes function within a span, returns result and span
        /// </summary>
        public (T Result, TracingSpan Span) ExecuteInSpan<T>(string name, Func<T> func, string kind = "internal")
        {
            var span = CreateSpan(name, kind);
            try
            {
                var result = func();
                span.End();
                return (result, span);
            }
            catch (Exception ex)
            {
                span.RecordException(ex);
                span.End(SpanStatus.Error, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Removes a span from active list
        /// </summary>
        internal void RemoveSpan(TracingSpan span)
        {
            lock (_activeLock)
            {
                _activeSpans.Remove(span);
            }
        }

        /// <summary>
        /// Scope that manages span lifetime
        /// </summary>
        private sealed class SpanScope : IDisposable
        {
            private readonly TracingSpan _span;
            private readonly SpanFactory _factory;
            private bool _disposed;

            public SpanScope(TracingSpan span, SpanFactory factory)
            {
                _span = span;
                _factory = factory;
            }

            public void Dispose()
            {
                if (_disposed) return;
                _disposed = true;

                if (_span.Status == SpanStatus.Unset)
                {
                    _span.End(SpanStatus.Ok);
                }

                _factory.RemoveSpan(_span);
            }
        }
    }
}
