﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.LogHub;
using Microsoft.VisualStudio.RpcContracts.Logging;
using Microsoft.VisualStudio.Shell.ServiceBroker;
using StreamJsonRpc;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageClient
{
    [Export(typeof(ILspServiceLoggerFactory))]
    internal class VisualStudioLogHubLoggerFactory : ILspServiceLoggerFactory
    {
        /// <summary>
        /// A unique, always increasing, ID we use to identify this server in our loghub logs.  Needed so that if our
        /// server is restarted that we can have a new logstream for the new server.
        /// </summary>
        private static int s_logHubSessionId;

        private readonly IVsService<IBrokeredServiceContainer> _brokeredServiceContainer;

        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public VisualStudioLogHubLoggerFactory(
            IVsService<SVsBrokeredServiceContainer, IBrokeredServiceContainer> brokeredServiceContainer)
        {
            _brokeredServiceContainer = brokeredServiceContainer;
        }

        public async Task<ILspServiceLogger> CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken)
        {
            var logName = $"Roslyn.{serverTypeName}.{Interlocked.Increment(ref s_logHubSessionId)}";
            var logId = new LogId(logName, new ServiceMoniker(typeof(AbstractLanguageServer<>).FullName));

            var serviceContainer = await _brokeredServiceContainer.GetValueAsync(cancellationToken).ConfigureAwait(false);
            var service = serviceContainer.GetFullAccessServiceBroker();

            var configuration = await TraceConfiguration.CreateTraceConfigurationInstanceAsync(service, ownsServiceBroker: true, cancellationToken).ConfigureAwait(false);

            // Register the default log level as information.
            // Loghub will take care of cleaning up older logs from past sessions / current session
            // if it decides the log file sizes are too large.
            var loggingLevel = SourceLevels.ActivityTracing | SourceLevels.Information;

            var logOptions = new RpcContracts.Logging.LoggerOptions(new LoggingLevelSettings(loggingLevel));
            var traceSource = await configuration.RegisterLogSourceAsync(logId, logOptions, cancellationToken).ConfigureAwait(false);

            // Associate this trace source with the jsonrpc conduit.  This ensures that we can associate logs we report
            // with our callers and the operations they are performing.
            jsonRpc.ActivityTracingStrategy = new CorrelationManagerTracingStrategy { TraceSource = traceSource };

            return new LogHubLspLogger(configuration, traceSource);
        }
    }
}
