﻿// 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.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServices.Xaml.Telemetry;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer
{
    internal class XamlRequestExecutionQueue : RequestExecutionQueue<RequestContext>, ILspService
    {
        private readonly XamlProjectService _projectService;
        private readonly IXamlLanguageServerFeedbackService? _feedbackService;

        public XamlRequestExecutionQueue(
            XamlProjectService projectService,
            IXamlLanguageServerFeedbackService? feedbackService,
            AbstractLanguageServer<RequestContext> languageServer,
            ILspLogger logger,
            IHandlerProvider handlerProvider) : base(languageServer, logger, handlerProvider)
        {
            _projectService = projectService;
            _feedbackService = feedbackService;
        }

        public override async Task<TResponseType> ExecuteAsync<TRequestType, TResponseType>(
            TRequestType request,
            string methodName,
            ILspServices lspServices,
            CancellationToken cancellationToken)
        {
            var methodHandler = GetMethodHandler<TRequestType, TResponseType>(methodName);
            TextDocumentIdentifier? textDocument = null;
            if (methodHandler is ITextDocumentIdentifierHandler txtDocumentIdentifierHandler)
            {
                if (txtDocumentIdentifierHandler is ITextDocumentIdentifierHandler<TRequestType, TextDocumentIdentifier> t)
                {
                    textDocument = t.GetTextDocumentIdentifier(request);
                }
            }

            DocumentId? documentId = null;
            if (textDocument is { Uri: { IsAbsoluteUri: true } documentUri })
            {
                documentId = _projectService.TrackOpenDocument(documentUri.LocalPath);
            }

            using (var requestScope = _feedbackService?.CreateRequestScope(documentId, methodName))
            {
                try
                {
                    return await base.ExecuteAsync<TRequestType, TResponseType>(
                        request, methodName, lspServices, cancellationToken).ConfigureAwait(false);
                }
                catch (Exception e) when (e is not OperationCanceledException)
                {
                    // Inform Xaml language service that the RequestScope failed.
                    // This doesn't send the exception to Telemetry or Watson
                    requestScope?.RecordFailure(e);
                    throw;
                }
            }
        }
    }
}
