﻿// 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.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ChangeSignature
{
    internal sealed class SignatureChange
    {
        public readonly ParameterConfiguration OriginalConfiguration;
        public readonly ParameterConfiguration UpdatedConfiguration;

        private readonly Dictionary<int, int?> _originalIndexToUpdatedIndexMap = new();

        public SignatureChange(ParameterConfiguration originalConfiguration, ParameterConfiguration updatedConfiguration)
        {
            OriginalConfiguration = originalConfiguration;
            UpdatedConfiguration = updatedConfiguration;

            // TODO: Could be better than O(n^2)
            var originalParameterList = originalConfiguration.ToListOfParameters();
            var updatedParameterList = updatedConfiguration.ToListOfParameters();

            for (var i = 0; i < originalParameterList.Length; i++)
            {
                int? index = null;
                var parameter = originalParameterList[i];
                if (parameter is ExistingParameter existingParameter)
                {
                    var updatedIndex = updatedParameterList.IndexOf(p => p is ExistingParameter ep && ep.Symbol.Equals(existingParameter.Symbol));
                    if (updatedIndex >= 0)
                    {
                        index = updatedIndex;
                    }
                }

                _originalIndexToUpdatedIndexMap.Add(i, index);
            }
        }

        public int? GetUpdatedIndex(int parameterIndex)
        {
            if (parameterIndex >= OriginalConfiguration.ToListOfParameters().Length)
            {
                return null;
            }

            return _originalIndexToUpdatedIndexMap[parameterIndex];
        }

        internal SignatureChange WithoutAddedParameters()
            => new(OriginalConfiguration, UpdatedConfiguration.WithoutAddedParameters());

        internal void LogTelemetry()
        {
            var originalListOfParameters = OriginalConfiguration.ToListOfParameters();
            var updatedListOfParameters = UpdatedConfiguration.ToListOfParameters();

            ChangeSignatureLogger.LogTransformationInformation(
                numOriginalParameters: originalListOfParameters.Length,
                numParametersAdded: updatedListOfParameters.Count(p => p is AddedParameter),
                numParametersRemoved: originalListOfParameters.Count(p => !updatedListOfParameters.Contains(p)),
                anyParametersReordered: AnyParametersReordered(originalListOfParameters, updatedListOfParameters));

            foreach (var addedParameter in updatedListOfParameters.OfType<AddedParameter>())
            {
                if (addedParameter.IsRequired)
                {
                    ChangeSignatureLogger.LogAddedParameterRequired();
                }

                if (addedParameter.TypeBinds)
                {
                    ChangeSignatureLogger.LogAddedParameterTypeBinds();
                }

                if (addedParameter.CallSiteKind == CallSiteKind.Todo)
                {
                    ChangeSignatureLogger.LogAddedParameter_ValueTODO();
                }
                else if (addedParameter.CallSiteKind == CallSiteKind.Omitted)
                {
                    ChangeSignatureLogger.LogAddedParameter_ValueOmitted();
                }
                else
                {
                    if (addedParameter.CallSiteKind == CallSiteKind.ValueWithName)
                    {
                        ChangeSignatureLogger.LogAddedParameter_ValueExplicitNamed();
                    }
                    else
                    {
                        ChangeSignatureLogger.LogAddedParameter_ValueExplicit();
                    }
                }
            }
        }

        private static bool AnyParametersReordered(ImmutableArray<Parameter> originalListOfParameters, ImmutableArray<Parameter> updatedListOfParameters)
        {
            var originalListWithoutRemovedOrAdded = originalListOfParameters.Where(updatedListOfParameters.Contains).ToImmutableArray();
            var updatedListWithoutRemovedOrAdded = updatedListOfParameters.Where(originalListOfParameters.Contains).ToImmutableArray();

            for (var i = 0; i < originalListWithoutRemovedOrAdded.Length; i++)
            {
                if (originalListWithoutRemovedOrAdded[i] != updatedListWithoutRemovedOrAdded[i])
                {
                    return true;
                }
            }

            return false;
        }
    }
}
