using AWS.Logger;
using System;
using System.Linq;

// Placed in the Microsoft namespaces so that the extension methods are visible whenever the owning namespace
// is declared.
namespace Microsoft.Extensions.Configuration
{
    /// <summary>
    /// Extensions methods for IConfiguration to lookup AWS logger configuration
    /// </summary>
    public static class ConfigurationSectionExtensions
    {
        // Default configuration block on the appsettings.json
        // Customer's information will be fetched from this block unless otherwise set.
        private const string DEFAULT_BLOCK = "Logging";

        // This library was originally written before logging standarized, or it at least we didn't realize it was standarized, on the "Logging" section in the config.
        // The library now uses "Logging" as the default section to look for config but to maintain backwards compatibility the package will fallback
        // AWS.Logging if a log group is not configured in the "Logging" config block".
        private const string LEGACY_DEFAULT_BLOCK = "AWS.Logging";

        /// <summary>
        /// Loads the AWS Logger Configuration from the ConfigSection
        /// </summary>
        /// <param name="configSection">ConfigSection</param>
        /// <param name="configSectionInfoBlockName">ConfigSection SubPath to load from</param>
        /// <returns></returns>
        public static AWSLoggerConfigSection GetAWSLoggingConfigSection(this IConfiguration configSection, string configSectionInfoBlockName = DEFAULT_BLOCK)
        {
            var loggerConfigSection = configSection.GetSection(configSectionInfoBlockName);
            AWSLoggerConfigSection configObj = null;
            if (loggerConfigSection[AWSLoggerConfigSection.LOG_GROUP] != null)
            {
                configObj = new AWSLoggerConfigSection(loggerConfigSection);
            }
            // If the code was relying on the default config block and no log group was found then
            // check the legacy default block.
            else if(string.Equals(configSectionInfoBlockName, DEFAULT_BLOCK, StringComparison.InvariantCulture))
            {
                loggerConfigSection = configSection.GetSection(LEGACY_DEFAULT_BLOCK);
                if (loggerConfigSection[AWSLoggerConfigSection.LOG_GROUP] != null)
                {
                    configObj = new AWSLoggerConfigSection(loggerConfigSection);
                }
            }


            return configObj;
        }
    }

    /// <summary>
    /// This class stores the configuration section information to connect to AWS and how the messages should be sent and the LogLevel section details
    /// </summary>
    public class AWSLoggerConfigSection
    {
        /// <summary>
        /// Configuration options for logging messages to AWS
        /// </summary>
        public AWSLoggerConfig Config { get; set; } = new AWSLoggerConfig();

        /// <summary>
        /// Custom LogLevel Filters for <see cref="AWS.Logger.AspNetCore.AWSLoggerProvider"/>
        /// </summary>
        public IConfiguration LogLevels { get; set; } = null;


        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeScopes"/> property. This determines if scopes - if they exist - are included in a log message.
        /// <para>
        /// The default is false.
        /// </para>
        /// </summary>
        public bool IncludeScopes { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeScopesDefault;

        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeLogLevel"/> property. This determines if log level is included in a log message.
        /// <para>
        /// The default is true.
        /// </para>
        /// </summary>
        public bool IncludeLogLevel { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeLogLevelDefault;

        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeCategory"/> property. This determines if category is included in a log message.
        /// <para>
        /// The default is true.
        /// </para>
        /// </summary>
        public bool IncludeCategory { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeCategoryDefault;

        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeEventId"/> property. This determines if event id is included in a log message.
        /// <para>
        /// The default is false.
        /// </para>
        /// </summary>
        public bool IncludeEventId { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeEventIdDefault;

        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeNewline"/> property. This determines if a new line is added to the end of the log message.
        /// <para>
        /// The default is true.
        /// </para>
        /// </summary>
        public bool IncludeNewline { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeNewlineDefault;

        /// <summary>
        /// Gets the <see cref="AWS.Logger.AspNetCore.AWSLogger.IncludeException"/> property. This determines if exceptions are included in a log message.
        /// <para>
        /// The default is false.
        /// </para>
        /// </summary>
        public bool IncludeException { get; set; } = AWS.Logger.AspNetCore.Constants.IncludeExceptionDefault;

        internal const string LOG_GROUP = "LogGroup";
        internal const string DISABLE_LOG_GROUP_CREATION = "DisableLogGroupCreation";
        internal const string REGION = "Region";
        internal const string SERVICEURL = "ServiceUrl";
        internal const string PROFILE = "Profile";
        internal const string PROFILE_LOCATION = "ProfilesLocation";
        internal const string BATCH_PUSH_INTERVAL = "BatchPushInterval";
        internal const string BATCH_PUSH_SIZE_IN_BYTES = "BatchPushSizeInBytes";
        internal const string LOG_LEVEL = "LogLevel";
        internal const string MAX_QUEUED_MESSAGES = "MaxQueuedMessages";
        internal const string LOG_STREAM_NAME_SUFFIX = "LogStreamNameSuffix";
        internal const string LOG_STREAM_NAME_PREFIX = "LogStreamNamePrefix";
        internal const string LIBRARY_LOG_FILE_NAME = "LibraryLogFileName";
        internal const string LIBRARY_LOG_ERRORS = "LibraryLogErrors";
        internal const string FLUSH_TIMEOUT = "FlushTimeout";

        private const string INCLUDE_LOG_LEVEL_KEY = "IncludeLogLevel";
        private const string INCLUDE_CATEGORY_KEY = "IncludeCategory";
        private const string INCLUDE_NEWLINE_KEY = "IncludeNewline";
        private const string INCLUDE_EXCEPTION_KEY = "IncludeException";
        private const string INCLUDE_EVENT_ID_KEY = "IncludeEventId";
        private const string INCLUDE_SCOPES_KEY = "IncludeScopes";

        /// <summary>
        /// Construct an instance of AWSLoggerConfigSection
        /// </summary>
        /// <param name="loggerConfigSection">ConfigSection to parse</param>
        public AWSLoggerConfigSection(IConfiguration loggerConfigSection)
        {
            Config.LogGroup = loggerConfigSection[LOG_GROUP];
            Config.DisableLogGroupCreation = loggerConfigSection.GetValue<bool>(DISABLE_LOG_GROUP_CREATION);
            if (loggerConfigSection[REGION] != null)
            {
                Config.Region = loggerConfigSection[REGION];
            }
            if (loggerConfigSection[SERVICEURL] != null)
            {
                Config.ServiceUrl = loggerConfigSection[SERVICEURL];
            }
            if (loggerConfigSection[PROFILE] != null)
            {
                Config.Profile = loggerConfigSection[PROFILE];
            }
            if (loggerConfigSection[PROFILE_LOCATION] != null)
            {
                Config.ProfilesLocation = loggerConfigSection[PROFILE_LOCATION];
            }
            if (loggerConfigSection[BATCH_PUSH_INTERVAL] != null)
            {
                Config.BatchPushInterval = TimeSpan.FromMilliseconds(Int32.Parse(loggerConfigSection[BATCH_PUSH_INTERVAL]));
            }
            if (loggerConfigSection[BATCH_PUSH_SIZE_IN_BYTES] != null)
            {
                Config.BatchSizeInBytes = Int32.Parse(loggerConfigSection[BATCH_PUSH_SIZE_IN_BYTES]);
            }
            if (loggerConfigSection[MAX_QUEUED_MESSAGES] != null)
            {
                Config.MaxQueuedMessages = Int32.Parse(loggerConfigSection[MAX_QUEUED_MESSAGES]);
            }
            if (loggerConfigSection[LOG_STREAM_NAME_SUFFIX] != null)
            {
                Config.LogStreamNameSuffix = loggerConfigSection[LOG_STREAM_NAME_SUFFIX];
            }
            if (loggerConfigSection[LOG_STREAM_NAME_PREFIX] != null)
            {
                Config.LogStreamNamePrefix = loggerConfigSection[LOG_STREAM_NAME_PREFIX];
            }
            if (loggerConfigSection[LIBRARY_LOG_FILE_NAME] != null)
            {
                Config.LibraryLogFileName = loggerConfigSection[LIBRARY_LOG_FILE_NAME];
            }
            if (loggerConfigSection[LIBRARY_LOG_ERRORS] != null)
            {
                Config.LibraryLogErrors = Boolean.Parse(loggerConfigSection[LIBRARY_LOG_ERRORS]);
            }
            if (loggerConfigSection[FLUSH_TIMEOUT] != null)
            {
                Config.FlushTimeout = TimeSpan.FromMilliseconds(Int32.Parse(loggerConfigSection[FLUSH_TIMEOUT]));
            }

            if (loggerConfigSection[INCLUDE_LOG_LEVEL_KEY] != null)
            {
                this.IncludeLogLevel = Boolean.Parse(loggerConfigSection[INCLUDE_LOG_LEVEL_KEY]);
            }
            if (loggerConfigSection[INCLUDE_CATEGORY_KEY] != null)
            {
                this.IncludeCategory = Boolean.Parse(loggerConfigSection[INCLUDE_CATEGORY_KEY]);
            }
            if (loggerConfigSection[INCLUDE_NEWLINE_KEY] != null)
            {
                this.IncludeNewline = Boolean.Parse(loggerConfigSection[INCLUDE_NEWLINE_KEY]);
            }
            if (loggerConfigSection[INCLUDE_EXCEPTION_KEY] != null)
            {
                this.IncludeException = Boolean.Parse(loggerConfigSection[INCLUDE_EXCEPTION_KEY]);
            }
            if (loggerConfigSection[INCLUDE_EVENT_ID_KEY] != null)
            {
                this.IncludeEventId = Boolean.Parse(loggerConfigSection[INCLUDE_EVENT_ID_KEY]);
            }
            if (loggerConfigSection[INCLUDE_SCOPES_KEY] != null)
            {
                this.IncludeScopes = Boolean.Parse(loggerConfigSection[INCLUDE_SCOPES_KEY]);
            }

            var logLevels = loggerConfigSection.GetSection(LOG_LEVEL);
            if (logLevels?.GetChildren().Any() == true)
            {
                this.LogLevels = logLevels;
            }
        }
    }
}