using Serilog.Core;
using Serilog.Events;
using Serilog.Parsing;

namespace WebApp.Logging
{
    /// <summary>
    /// Custom Serilog sink that downgrades antiforgery token deserialization errors to Information level
    /// </summary>
    public class AntiforgeryLogEventSink : ILogEventSink
    {
        private readonly ILogEventSink _wrappedSink;
        private readonly MessageTemplateParser _parser;

        public AntiforgeryLogEventSink(ILogEventSink wrappedSink)
        {
            _wrappedSink = wrappedSink;
            _parser = new MessageTemplateParser();
        }

        public void Emit(LogEvent logEvent)
        {
            // Check if this is an antiforgery token deserialization error (Event ID 7)
            if (IsTokenDeserializeException(logEvent))
            {
                // Rewrite the log event at Information level with explanatory message
                var messageTemplate = _parser.Parse(
                    "Antiforgery token validation failed - expected after container restart. " +
                    "User will receive a fresh token on page refresh. Original message: {OriginalMessage}");

                var rewrittenEvent = new LogEvent(
                    logEvent.Timestamp,
                    LogEventLevel.Information,
                    null, // No exception at Info level
                    messageTemplate,
                    new[]
                    {
                        new LogEventProperty("OriginalMessage",
                            new ScalarValue(logEvent.MessageTemplate.Render(logEvent.Properties))),
                        new LogEventProperty("SourceContext",
                            logEvent.Properties.GetValueOrDefault("SourceContext") ?? new ScalarValue("Unknown")),
                        new LogEventProperty("RequestPath",
                            logEvent.Properties.GetValueOrDefault("RequestPath") ?? new ScalarValue("Unknown"))
                    });

                _wrappedSink.Emit(rewrittenEvent);
            }
            else
            {
                // Pass through all other log events unchanged
                _wrappedSink.Emit(logEvent);
            }
        }

        private bool IsTokenDeserializeException(LogEvent logEvent)
        {
            if (logEvent.Level != LogEventLevel.Error)
                return false;

            if (logEvent.Properties.TryGetValue("EventId", out var eventId))
            {
                var eventIdString = eventId.ToString();
                return eventIdString.Contains("\"Id\":7") &&
                       eventIdString.Contains("\"Name\":\"TokenDeserializeException\"");
            }

            return false;
        }
    }
}
