﻿// 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.Composition;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Rename;

namespace Microsoft.CodeAnalysis.CSharp.Rename
{
    [ExportLanguageService(typeof(IRenameIssuesService), LanguageNames.CSharp), Shared]
    internal sealed class CSharpRenameIssuesService : IRenameIssuesService
    {
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public CSharpRenameIssuesService()
        {
        }

        public bool CheckLanguageSpecificIssues(
            SemanticModel semanticModel, ISymbol symbol, SyntaxToken triggerToken, [NotNullWhen(true)] out string? langError)
        {
            if (triggerToken.IsTypeNamedDynamic() &&
                symbol.Kind == SymbolKind.DynamicType)
            {
                langError = FeaturesResources.You_cannot_rename_this_element;
                return true;
            }

            if (IsTypeNamedVarInVariableOrFieldDeclaration(triggerToken))
            {
                // To check if var in this context is a real type, or the keyword, we need to 
                // speculatively bind the identifier "var". If it returns a symbol, it's a real type,
                // if not, it's the keyword.
                // see bugs 659683 (compiler API) and 659705 (rename/workspace api) for examples
                var symbolForVar = semanticModel.GetSpeculativeSymbolInfo(
                    triggerToken.SpanStart,
                    triggerToken.Parent!,
                    SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol;

                if (symbolForVar == null)
                {
                    langError = FeaturesResources.You_cannot_rename_this_element;
                    return true;
                }
            }

            langError = null;
            return false;
        }

        private static bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token)
        {
            var parent = token.Parent;
            if (parent.IsKind(SyntaxKind.IdentifierName))
            {
                TypeSyntax? declaredType = null;
                if (parent?.Parent is VariableDeclarationSyntax varDecl)
                {
                    declaredType = varDecl.Type;
                }
                else if (parent?.Parent is FieldDeclarationSyntax fieldDecl)
                {
                    declaredType = fieldDecl.Declaration.Type;
                }

                return declaredType == parent && token.Text == "var";
            }

            return false;
        }
    }
}
