// 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.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.CodeAnalysis.CSharp.UsePatternMatching
{
    internal static class UsePatternMatchingHelpers
    {
        public static bool TryGetPartsOfAsAndMemberAccessCheck(
            BinaryExpressionSyntax asExpression,
            [NotNullWhen(true)] out ConditionalAccessExpressionSyntax? conditionalAccessExpression,
            out BinaryExpressionSyntax? binaryExpression,
            out IsPatternExpressionSyntax? isPatternExpression,
            out LanguageVersion requiredLanguageVersion)
        {
            conditionalAccessExpression = null;
            binaryExpression = null;
            isPatternExpression = null;
            requiredLanguageVersion = LanguageVersion.CSharp8;

            if (asExpression.Kind() == SyntaxKind.AsExpression)
            {
                // has to be `(expr as T)`
                if (asExpression.Parent is not ParenthesizedExpressionSyntax
                    {
                        // Has to be `(expr as T)?...`
                        Parent: ConditionalAccessExpressionSyntax parentConditionalAccess
                    })
                {
                    return false;
                }

                conditionalAccessExpression = parentConditionalAccess;

                // After the `?` has to be `.X.Y.Z`

                var whenNotNull = parentConditionalAccess.WhenNotNull;
                while (whenNotNull is MemberAccessExpressionSyntax memberAccess)
                {
                    // Extended property patterns are only in 10 and up.
                    requiredLanguageVersion = LanguageVersion.CSharp10;
                    whenNotNull = memberAccess.Expression;
                }

                // Ensure we have `.X`
                if (whenNotNull is not MemberBindingExpressionSyntax)
                    return false;

                if (conditionalAccessExpression.Parent is BinaryExpressionSyntax(SyntaxKind.EqualsExpression) parentBinaryExpression1 &&
                    parentBinaryExpression1.Left == conditionalAccessExpression)
                {
                    // `(expr as T)?... == other_expr
                    //
                    // Can convert if other_expr is a constant (checked by caller).
                    binaryExpression = parentBinaryExpression1;
                    return true;
                }
                else if (conditionalAccessExpression.Parent is
                        BinaryExpressionSyntax(
                            SyntaxKind.NotEqualsExpression or
                            SyntaxKind.GreaterThanExpression or
                            SyntaxKind.GreaterThanOrEqualExpression or
                            SyntaxKind.LessThanExpression or
                            SyntaxKind.LessThanOrEqualExpression) parentBinaryExpression2 &&
                    parentBinaryExpression2.Left == conditionalAccessExpression)
                {
                    // `(expr as T)?... != other_expr
                    //
                    // Can convert if other_expr is a constant (checked by caller).
                    binaryExpression = parentBinaryExpression2;

                    // relational patterns need c# 9 or above.
                    requiredLanguageVersion = (LanguageVersion)Math.Max((int)requiredLanguageVersion, (int)LanguageVersion.CSharp9);
                    return true;
                }
                else if (conditionalAccessExpression.Parent is IsPatternExpressionSyntax parentIsPatternExpression)
                {
                    // `(expr as T)?... is pattern`
                    //
                    //  We can convert this to a pattern in most cases except for:
                    //
                    // `(expr as T)?... is not X y`.  As it is not legal to transform into `expr is T { Member: not X y }`.
                    // Specifically, `not variable` patterns are not legal except at the top level of a pattern.
                    if (parentIsPatternExpression.Pattern is UnaryPatternSyntax
                        {
                            Pattern: DeclarationPatternSyntax or VarPatternSyntax or RecursivePatternSyntax { Designation: not null }
                        })
                    {
                        return false;
                    }

                    isPatternExpression = parentIsPatternExpression;
                    return true;
                }
            }

            return false;
        }
    }
}
