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

using Microsoft.ApplicationInsights.Channel;
using IChannelTelemetry = Microsoft.ApplicationInsights.Channel.ITelemetry;

namespace Microsoft.DotNet.Cli.Telemetry.PersistenceChannel
{
    /// <summary>
    ///     Represents a communication channel for sending telemetry to Application Insights via HTTPS.
    /// </summary>
    internal sealed class PersistenceChannel : ITelemetryChannel
    {
        internal const string TelemetryServiceEndpoint = "https://dc.services.visualstudio.com/v2/track";

        private readonly FlushManager _flushManager;

        private int _disposeCount;
        private readonly BaseStorageService _storage;
        private readonly PersistenceTransmitter _transmitter;

        /// <summary>
        ///     Initializes a new instance of the <see cref="PersistenceChannel" /> class.
        /// </summary>
        /// <param name="storageDirectoryPath">
        ///     Full path of a directory name. Under this folder all the transmissions will be saved.
        ///     Setting this value groups channels, even from different processes.
        ///     If 2 (or more) channels has the same <c>storageFolderName</c> only one channel will perform the sending even if the
        ///     channel is in a different process/AppDomain/Thread.
        /// </param>
        /// <param name="sendersCount">
        ///     Defines the number of senders. A sender is a long-running thread that sends telemetry batches in intervals defined
        ///     by <see cref="SendingInterval" />.
        ///     So the amount of senders also defined the maximum amount of http channels opened at the same time.
        /// </param>
        public PersistenceChannel(string storageDirectoryPath = null, int sendersCount = 1)
        {
            _storage = new StorageService();
            _storage.Init(storageDirectoryPath);
            _transmitter = new PersistenceTransmitter(_storage, sendersCount);
            _flushManager = new FlushManager(_storage);
            EndpointAddress = TelemetryServiceEndpoint;
        }

        /// <summary>
        ///     Gets or sets an interval between each successful sending.
        /// </summary>
        /// <remarks>
        ///     On error scenario this value is ignored and the interval will be defined using an exponential back-off
        ///     algorithm.
        /// </remarks>
        public TimeSpan? SendingInterval
        {
            get => _transmitter.SendingInterval;
            set => _transmitter.SendingInterval = value;
        }


        /// <summary>
        ///     Gets or sets the maximum amount of files allowed in storage. When the limit is reached telemetries will be dropped.
        /// </summary>
        public uint MaxTransmissionStorageFilesCapacity
        {
            get => _storage.MaxFiles;
            set => _storage.MaxFiles = value;
        }

        /// <summary>
        ///     This flag has no effect. But it is required by base class
        /// </summary>
        public bool? DeveloperMode { get; set; }

        /// <summary>
        ///     Gets or sets the HTTP address where the telemetry is sent.
        /// </summary>
        public string EndpointAddress
        {
            get => _flushManager.EndpointAddress.ToString();

            set
            {
                string address = value ?? TelemetryServiceEndpoint;
                _flushManager.EndpointAddress = new Uri(address);
            }
        }

        /// <summary>
        ///     Releases unmanaged and - optionally - managed resources.
        /// </summary>
        public void Dispose()
        {
            if (Interlocked.Increment(ref _disposeCount) == 1)
            {
                _transmitter?.Dispose();
            }
        }

        /// <summary>
        ///     Sends an instance of ITelemetry through the channel.
        /// </summary>
        public void Send(IChannelTelemetry item)
        {
            _flushManager.Flush(item);
        }

        /// <summary>
        ///     No operation, send will always flush. So nothing will be in memory
        /// </summary>
        public void Flush()
        {
        }
    }
}
