// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.HttpLogging
{
    internal class W3CLoggerProcessor : FileLoggerProcessor
    {
        private readonly W3CLoggingFields _loggingFields;

        public W3CLoggerProcessor(IOptionsMonitor<W3CLoggerOptions> options, IHostEnvironment environment, ILoggerFactory factory) : base(options, environment, factory)
        {
            _loggingFields = options.CurrentValue.LoggingFields;
        }

        public override async Task OnFirstWrite(StreamWriter streamWriter, CancellationToken cancellationToken)
        {
            await WriteMessageAsync("#Version: 1.0", streamWriter, cancellationToken);

            await WriteMessageAsync("#Start-Date: " + DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), streamWriter, cancellationToken);

            await WriteMessageAsync(GetFieldsDirective(), streamWriter, cancellationToken);
        }

        private string GetFieldsDirective()
        {
            // 152 is the length of the default fields directive
            var sb = new ValueStringBuilder(152);
            sb.Append("#Fields:");
            if (_loggingFields.HasFlag(W3CLoggingFields.Date))
            {
                sb.Append(" date");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.Time))
            {
                sb.Append(" time");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ClientIpAddress))
            {
                sb.Append(" c-ip");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.UserName))
            {
                sb.Append(" cs-username");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ServerName))
            {
                sb.Append(" s-computername");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ServerIpAddress))
            {
                sb.Append(" s-ip");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ServerPort))
            {
                sb.Append(" s-port");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.Method))
            {
                sb.Append(" cs-method");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.UriStem))
            {
                sb.Append(" cs-uri-stem");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.UriQuery))
            {
                sb.Append(" cs-uri-query");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ProtocolStatus))
            {
                sb.Append(" sc-status");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.TimeTaken))
            {
                sb.Append(" time-taken");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.ProtocolVersion))
            {
                sb.Append(" cs-version");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.Host))
            {
                sb.Append(" cs-host");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.UserAgent))
            {
                sb.Append(" cs(User-Agent)");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.Cookie))
            {
                sb.Append(" cs(Cookie)");
            }
            if (_loggingFields.HasFlag(W3CLoggingFields.Referer))
            {
                sb.Append(" cs(Referer)");
            }

            return sb.ToString();
        }

        // For testing
        internal override Task WriteMessageAsync(string message, StreamWriter streamWriter, CancellationToken cancellationToken)
        {
            OnWrite(message);
            return base.WriteMessageAsync(message, streamWriter, cancellationToken);
        }

        // Extensibility point for tests
        internal virtual void OnWrite(string message) { }
    }
}
