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

using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Razor.FindAllReferences;

internal static class FindAllReferencesHelper
{
    public static async Task<string?> GetResultTextAsync(IDocumentMappingService documentMappingService, ISolutionQueryOperations solutionQueryOperations, int lineNumber, string filePath, CancellationToken cancellationToken)
    {
        // Roslyn will have sent us back text that comes from the .g.cs file, but that is often not helpful. For example give:
        //
        // <SurveyPrompt Title="Blah" />
        //
        // A FAR for the Title property will return just the word "Title" in the Text of the reference item, which does not
        // help the user reason about the result. For such cases, its better to return the text from the Razor file, even
        // though it will be unclassified, as it will help the user.
        //
        // However, for cases where the result comes from a C# block, for example:
        //
        // @code {
        //    public string Title { get; set; }
        // }
        //
        // A FAR for the Title property here will return the full line of code, classified by Roslyn, so we don't want to
        // do anything for those.
        //
        // To identify which situation we're in, we try to map the start and the end of the line to C#, as an indicator. If
        // either start or end fail to map, it means the entire line is not C#

        // TODO: Note the call to ISolutionQueryOperations.GetProjectsContainingDocument(...) will be removed with the introduction of solution snapshots.
        if (solutionQueryOperations.GetProjectsContainingDocument(filePath).FirstOrDefault() is { } project &&
            project.TryGetDocument(filePath, out var document))
        {
            var codeDoc = await document.GetGeneratedOutputAsync(cancellationToken).ConfigureAwait(false);
            var line = codeDoc.Source.Text.Lines[lineNumber];
            var csharpDocument = codeDoc.GetRequiredCSharpDocument();
            if (!documentMappingService.TryMapToCSharpDocumentPosition(csharpDocument, line.Start, out _, out _) ||
                !documentMappingService.TryMapToCSharpDocumentPosition(csharpDocument, line.End, out _, out _))
            {
                var start = line.GetFirstNonWhitespacePosition() ?? line.Start;
                return codeDoc.Source.Text.GetSubTextString(TextSpan.FromBounds(start, line.End));
            }
        }

        return null;
    }
}
