using System;
using NLog;
using NLog.Targets;
using NLog.Common;
using NLog.Config;
using AWS.Logger;
using AWS.Logger.Core;
using Amazon.Runtime;
namespace NLog.AWS.Logger
{
    /// 
    /// An NLog target that can be used with the NLog logging library to send messages to AWS.
    /// 
    [Target("AWSTarget")]
    public class AWSTarget : TargetWithLayout, IAWSLoggerConfig
    {
        AWSLoggerConfig _config = new AWSLoggerConfig();
        AWSLoggerCore _core = null;
		
        /// 
        /// Default Constructor
        /// 
        public AWSTarget()
        {
            this.OptimizeBufferReuse = true;
        }
        /// 
        /// Gets and sets the LogGroup property. This is the name of the CloudWatch Logs group where 
        /// streams will be created and log messages written to.
        /// 
        [RequiredParameter]
        public string LogGroup
        {
            get { return _config.LogGroup; }
            set { _config.LogGroup = value; }
        }
        /// 
        /// Determines whether or not to create a new Log Group, if the one specified by  doesn't already exist
        /// 
        /// 
        public bool DisableLogGroupCreation
        {
            get { return _config.DisableLogGroupCreation; }
            set { _config.DisableLogGroupCreation = value; }
        }
        /// 
        /// Gets and sets the Profile property. The profile is used to look up AWS credentials in the profile store.
        /// 
        /// For understanding how credentials are determine view the top level documentation for AWSLoggerConfig class.
        /// 
        /// 
        public string Profile
        {
            get { return _config.Profile; }
            set { _config.Profile = value; }
        }
        /// 
        /// Gets and sets the ProfilesLocation property. If this is not set the default profile store is used by the AWS SDK for .NET 
        /// to look up credentials. This is most commonly used when you are running an application of on-priemse under a service account.
        /// 
        /// For understanding how credentials are determine view the top level documentation for AWSLoggerConfig class.
        /// 
        /// 
        public string ProfilesLocation
        {
            get { return _config.ProfilesLocation; }
            set { _config.ProfilesLocation = value; }
        }
        /// 
        /// Gets and sets the Credentials property. These are the AWS credentials used by the AWS SDK for .NET to make service calls.
        /// 
        /// For understanding how credentials are determine view the top level documentation for AWSLoggerConfig class.
        /// 
        /// 
        public AWSCredentials Credentials
        {
            get { return _config.Credentials; }
            set { _config.Credentials = value; }
        }
        /// 
        /// Gets and sets the Region property. This is the AWS Region that will be used for CloudWatch Logs. If this is not
        /// the AWS SDK for .NET will use its fall back logic to try and determine the region through environment variables and EC2 instance metadata.
        /// If the Region is not set and no region is found by the SDK's fall back logic then an exception will be thrown.
        /// 
        public string Region
        {
            get { return _config.Region; }
            set { _config.Region = value; }
        }
        /// 
        /// Gets and sets of the ServiceURL property. This is an optional property; change
        /// it only if you want to try a different service endpoint. Ex. for LocalStack
        /// 
        public string ServiceUrl
        {
            get { return _config.ServiceUrl; }
            set { _config.ServiceUrl = value; }
        }
        /// 
        /// Gets and sets the BatchPushInterval property. For performance the log messages are sent to AWS in batch sizes. BatchPushInterval 
        /// dictates the frequency of when batches are sent. If either BatchPushInterval or BatchSizeInBytes are exceeded the batch will be sent.
        /// 
        /// The default is 3 seconds.
        /// 
        /// 
        public TimeSpan BatchPushInterval
        {
            get { return _config.BatchPushInterval; }
            set { _config.BatchPushInterval = value; }
        }
        /// 
        /// Gets and sets the BatchSizeInBytes property. For performance the log messages are sent to AWS in batch sizes. BatchSizeInBytes 
        /// dictates the total size of the batch in bytes when batches are sent. If either BatchPushInterval or BatchSizeInBytes are exceeded the batch will be sent.
        /// 
        /// The default is 100 Kilobytes.
        /// 
        /// 
        public int BatchSizeInBytes
        {
            get { return _config.BatchSizeInBytes; }
            set { _config.BatchSizeInBytes = value; }
        }
        /// 
        /// Gets and sets the MaxQueuedMessages property. This specifies the maximum number of log messages that could be stored in-memory. MaxQueuedMessages 
        /// dictates the total number of log messages that can be stored in-memory. If this is exceeded, incoming log messages will be dropped.
        /// 
        /// The default is 10000.
        /// 
        /// 
        public int MaxQueuedMessages
        {
            get { return _config.MaxQueuedMessages; }
            set { _config.MaxQueuedMessages = value; }
        }
        /// 
        /// Gets and sets the LogStreamNameSuffix property. The LogStreamName consists of an optional user-defined prefix segment, then a DateTimeStamp as the
        /// system-defined prefix segment, and a user defined suffix value that can be set using the LogStreamNameSuffix property defined here.
        /// 
        /// The default is going to a Guid.
        /// 
        /// 
        public string LogStreamNameSuffix
        {
            get { return _config.LogStreamNameSuffix; }
            set { _config.LogStreamNameSuffix = value; }
        }
        /// 
        /// Gets and sets the LogStreamNamePrefix property. The LogStreamName consists of an optional user-defined prefix segment (defined here), then a
        /// DateTimeStamp as the system-defined prefix segment, and a user defined suffix value that can be set using the LogStreamNameSuffix property.
        /// 
        /// The default will use an empty string for this user-defined portion, meaning the log stream name will start with the system-defined portion of the prefix (yyyy/MM/dd ... )
        /// 
        /// 
        public string LogStreamNamePrefix
        {
            get { return _config.LogStreamNamePrefix; }
            set { _config.LogStreamNamePrefix = value; }
        }
        /// 
        /// Gets and sets the LibraryLogErrors property. This is the boolean value of whether or not you would like this library to log logging errors.
        /// 
        /// The default is "true".
        /// 
        /// 
        public bool LibraryLogErrors
        {
            get { return _config.LibraryLogErrors; }
            set { _config.LibraryLogErrors = value; }
        }
        /// 
        /// Gets and sets the LibraryLogFileName property. This is the name of the file into which errors from the AWS.Logger.Core library will be written into.
        /// 
        /// The default is "aws-logger-errors.txt".
        /// 
        /// 
        public string LibraryLogFileName
        {
            get { return _config.LibraryLogFileName; }
            set { _config.LibraryLogFileName = value; }
        }
        /// 
        /// Gets the FlushTimeout property. The value is in milliseconds. When performing a flush of the in-memory queue this is the maximum period of time allowed to send the remaining
        /// messages before it will be aborted. If this is exceeded, incoming log messages will be dropped.
        /// 
        /// The default is 30000 milliseconds.
        /// 
        /// 
        public TimeSpan FlushTimeout
        {
            get { return _config.FlushTimeout; }
            set { _config.FlushTimeout = value; }
        }
        /// 
        protected override void InitializeTarget()
        {
            if (_core != null)
            {
                _core.Close();
                _core = null;
            }
            var config = new AWSLoggerConfig(RenderSimpleLayout(LogGroup, nameof(LogGroup)))
            {
                DisableLogGroupCreation = DisableLogGroupCreation,
                Region = RenderSimpleLayout(Region, nameof(Region)),
                ServiceUrl = RenderSimpleLayout(ServiceUrl, nameof(ServiceUrl)),
                Credentials = Credentials,
                Profile = RenderSimpleLayout(Profile, nameof(Profile)),
                ProfilesLocation = RenderSimpleLayout(ProfilesLocation, nameof(ProfilesLocation)),
                BatchPushInterval = BatchPushInterval,
                BatchSizeInBytes = BatchSizeInBytes,
                MaxQueuedMessages = MaxQueuedMessages,
                LogStreamNameSuffix = RenderSimpleLayout(LogStreamNameSuffix, nameof(LogStreamNameSuffix)),
                LogStreamNamePrefix = RenderSimpleLayout(LogStreamNamePrefix, nameof(LogStreamNamePrefix)),
                LibraryLogErrors = LibraryLogErrors,
                LibraryLogFileName = LibraryLogFileName,
                FlushTimeout = FlushTimeout
            };
            _core = new AWSLoggerCore(config, "NLog");
            _core.LogLibraryAlert += AwsLogLibraryAlert;
        }
        private string RenderSimpleLayout(string simpleLayout, string propertyName)
        {
            try
            {
                return string.IsNullOrEmpty(simpleLayout) ? string.Empty : new Layouts.SimpleLayout(simpleLayout).Render(LogEventInfo.CreateNullEvent());
            }
            catch (Exception ex)
            {
                InternalLogger.Debug(ex, "AWSTarget(Name={0}) - Could not render Layout for {1}", Name, propertyName);
                return simpleLayout;
            }
        }
        private void AwsLogLibraryAlert(object sender, AWSLoggerCore.LogLibraryEventArgs e)
        {
            InternalLogger.Error(e.Exception, "AWSTarget(Name={0}) - CloudWatch Network Error - ServiceUrl={1}", Name, e.ServiceUrl);
        }
        /// 
        protected override void Write(LogEventInfo logEvent)
        {
            var message = RenderLogEvent(this.Layout, logEvent);
            _core.AddMessage(message);
        }
        /// 
        protected override void FlushAsync(AsyncContinuation asyncContinuation)
        {
            try
            {
                _core.Flush();
                asyncContinuation(null);
            }
            catch (Exception ex)
            {
                asyncContinuation(ex);
            }
        }
    }
}