// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;

namespace Microsoft.VisualStudio.Extensibility.Testing;

internal partial class EditorInProcess
{
    public async Task<IEnumerable<object>> HoverAsync(int position, CancellationToken cancellationToken)
    {
        var hoverService = await GetComponentModelServiceAsync<IAsyncQuickInfoBroker>(cancellationToken);

        var view = await GetActiveTextViewAsync(cancellationToken);

        var trackingPoint = view.TextSnapshot.CreateTrackingPoint(position, PointTrackingMode.Positive);
        var quickInfoSession = await hoverService.TriggerQuickInfoAsync(view, trackingPoint, QuickInfoSessionOptions.None, cancellationToken);

        using var semaphore = new SemaphoreSlim(1);
        await semaphore.WaitAsync(cancellationToken);

        if (quickInfoSession is null)
        {
            throw new InvalidOperationException("Quick Info Session failed to launch.");
        }

        quickInfoSession.StateChanged += QuickInfoSession_StateChanged;

        if (QuickInfoResolved(quickInfoSession))
        {
            semaphore.Release();
            quickInfoSession.StateChanged -= QuickInfoSession_StateChanged;
        }

        try
        {
            await semaphore.WaitAsync(cancellationToken);
        }
        finally
        {
            quickInfoSession.StateChanged -= QuickInfoSession_StateChanged;
        }

        return quickInfoSession.Content;

        static bool QuickInfoResolved(IAsyncQuickInfoSession quickInfoSession)
        {
            return quickInfoSession.State == QuickInfoSessionState.Visible;
        }

        void QuickInfoSession_StateChanged(object sender, QuickInfoSessionStateChangedEventArgs e)
        {
            if (e.NewState == QuickInfoSessionState.Visible)
            {
                semaphore.Release();
            }
        }
    }

    public async Task<string> GetHoverStringAsync(int position, CancellationToken cancellationToken)
    {
        var hoverContent = await HoverAsync(position, cancellationToken);

        using var _ = StringBuilderPool.GetPooledObject(out var sb);

        TraverseContent(hoverContent, sb);
        return sb.ToString();

        static void TraverseContent(IEnumerable<object> objects, StringBuilder stringBuilder)
        {
            foreach (var obj in objects)
            {
                switch (obj)
                {
                    case ImageElement imageElement:
                        break;
                    case ClassifiedTextElement textElement:
                        foreach (var run in textElement.Runs)
                        {
                            stringBuilder.Append(run.Text);
                        }

                        break;
                    case ContainerElement containerElement:
                        TraverseContent(containerElement.Elements, stringBuilder);
                        break;
                    default:
                        throw new NotImplementedException("Unknown QuickInfo element encountered");
                }
            }
        }
    }
}

