using EonaCat.LogStack.Core;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EonaCat.LogStack.Flows
{
    // 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>
    /// A flow that transforms log events before passing them to an inner flow.
    /// Allows modification of messages, properties, and other attributes.
    /// </summary>
    public sealed class TransformFlow : FlowBase
    {
        private readonly IFlow _innerFlow;
        private readonly Func<LogEvent, LogEvent> _transformer;

        public TransformFlow(
            IFlow innerFlow,
            Func<LogEvent, LogEvent> transformer,
            LogLevel minimumLevel = LogLevel.Trace)
            : base(innerFlow?.Name + "_Transform" ?? "TransformFlow", minimumLevel)
        {
            _innerFlow = innerFlow ?? throw new ArgumentNullException(nameof(innerFlow));
            _transformer = transformer ?? throw new ArgumentNullException(nameof(transformer));
        }

        public override async Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken cancellationToken = default)
        {
            if (!IsEnabled || !IsLogLevelEnabled(logEvent))
            {
                return WriteResult.LevelFiltered;
            }

            try
            {
                var transformedEvent = _transformer(logEvent);
                var result = await _innerFlow.BlastAsync(transformedEvent, cancellationToken).ConfigureAwait(false);
                if (result == WriteResult.Success)
                {
                    Interlocked.Increment(ref BlastedCount);
                }
                else
                {
                    Interlocked.Increment(ref DroppedCount);
                }

                return result;
            }
            catch (Exception ex)
            {
                Interlocked.Increment(ref DroppedCount);
                System.Diagnostics.Debug.WriteLine($"TransformFlow error: {ex.Message}");
                return WriteResult.Failed;
            }
        }

        public override async Task<WriteResult> BlastBatchAsync(ReadOnlyMemory<LogEvent> logEvents, CancellationToken cancellationToken = default)
        {
            if (!IsEnabled)
            {
                return WriteResult.FlowDisabled;
            }

            var eventsArray = logEvents.ToArray();
            var transformedEvents = new List<LogEvent>();
            foreach (var logEvent in eventsArray)
            {
                if (IsLogLevelEnabled(logEvent))
                {
                    try
                    {
                        var transformed = _transformer(logEvent);
                        transformedEvents.Add(transformed);
                        Interlocked.Increment(ref BlastedCount);
                    }
                    catch (Exception ex)
                    {
                        Interlocked.Increment(ref DroppedCount);
                        System.Diagnostics.Debug.WriteLine($"TransformFlow batch error: {ex.Message}");
                    }
                }
            }

            if (transformedEvents.Count > 0)
            {
                return await _innerFlow.BlastBatchAsync(transformedEvents.ToArray(), cancellationToken).ConfigureAwait(false);
            }

            return WriteResult.Success;
        }

        public override async Task FlushAsync(CancellationToken cancellationToken = default)
        {
            await _innerFlow.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        public override async ValueTask DisposeAsync()
        {
            await _innerFlow.DisposeAsync().ConfigureAwait(false);
            await base.DisposeAsync().ConfigureAwait(false);
        }
    }
}
