﻿// 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.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;

namespace Microsoft.CodeAnalysis.AddImport
{
    internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSyntax>
    {
        private readonly struct SearchResult(string? desiredName, TSimpleNameSyntax nameNode, IReadOnlyList<string> nameParts, double weight)
        {
            public readonly IReadOnlyList<string> NameParts = nameParts;

            // How good a match this was.  0 means it was a perfect match.  Larger numbers are less 
            // and less good.
            public readonly double Weight = weight;

            // The desired name to change the user text to if this was a fuzzy (spell-checking) match.
            public readonly string? DesiredName = desiredName;

            // The node to convert to the desired name
            public readonly TSimpleNameSyntax NameNode = nameNode;

            public SearchResult(SymbolResult<INamespaceOrTypeSymbol> result)
                : this(result.DesiredName, result.NameNode, INamespaceOrTypeSymbolExtensions.GetNameParts(result.Symbol), result.Weight)
            {
            }

            public bool DesiredNameDiffersFromSourceName()
            {
                return !string.IsNullOrEmpty(DesiredName) &&
                    NameNode != null &&
                    NameNode.GetFirstToken().ValueText != DesiredName;
            }

            public bool DesiredNameDiffersFromSourceNameOnlyByCase()
            {
                Debug.Assert(DesiredNameDiffersFromSourceName());
                return StringComparer.OrdinalIgnoreCase.Equals(
                    NameNode.GetFirstToken().ValueText, DesiredName);
            }

            public bool DesiredNameMatchesSourceName(Document document)
            {
                if (!DesiredNameDiffersFromSourceName())
                {
                    // Names match in any language.
                    return true;
                }

                var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();

                // Names differ.  But in a case insensitive language they may match.
                if (!syntaxFacts.IsCaseSensitive &&
                    DesiredNameDiffersFromSourceNameOnlyByCase())
                {
                    return true;
                }

                // Name are totally different in any language.
                return false;
            }
        }

        private readonly struct SymbolResult<T>(string desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol
        {
            // The symbol that matched the string being searched for.
            public readonly T Symbol = symbol;

            // How good a match this was.  0 means it was a perfect match.  Larger numbers are less 
            // and less good.
            public readonly double Weight = weight;

            // The desired name to change the user text to if this was a fuzzy (spell-checking) match.
            public readonly string DesiredName = desiredName;

            // The node to convert to the desired name
            public readonly TSimpleNameSyntax NameNode = nameNode;

            public SymbolResult<T2> WithSymbol<T2>(T2 symbol) where T2 : ISymbol
                => new(DesiredName, NameNode, symbol, Weight);

            internal SymbolResult<T> WithDesiredName(string desiredName)
                => new(desiredName, NameNode, Symbol, Weight);
        }

        private struct SymbolResult
        {
            public static SymbolResult<T> Create<T>(string desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol
                => new(desiredName, nameNode, symbol, weight);
        }
    }
}
