using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;

namespace RoslynPad.Roslyn.Completion.Providers
{
    [ExportCompletionProvider("ReferenceDirectiveCompletionProvider", LanguageNames.CSharp)]
    internal class ReferenceDirectiveCompletionProvider : AbstractReferenceDirectiveCompletionProvider
    {
        private static readonly CompletionItemRules s_rules = CompletionItemRules.Create(
            ImmutableArray<CharacterSetModificationRule>.Empty,
            ImmutableArray<CharacterSetModificationRule>.Empty,
            EnterKeyRule.Never,
            selectionBehavior: CompletionItemSelectionBehavior.SoftSelection);

        private readonly INuGetCompletionProvider _nuGetCompletionProvider;

        [ImportingConstructor]
        public ReferenceDirectiveCompletionProvider([Import(AllowDefault = true)] INuGetCompletionProvider nuGetCompletionProvider) =>
            _nuGetCompletionProvider = nuGetCompletionProvider;

        private CompletionItem CreateNuGetRoot()
            => CommonCompletionItem.Create(
                ReferenceDirectiveHelper.NuGetPrefix,
                "",
                s_rules,
                Microsoft.CodeAnalysis.Glyph.NuGet,
                sortText: "");

        protected override Task ProvideCompletionsAsync(CompletionContext context, string pathThroughLastSlash)
        {
            if (_nuGetCompletionProvider != null &&
                pathThroughLastSlash.StartsWith(ReferenceDirectiveHelper.NuGetPrefix, StringComparison.InvariantCultureIgnoreCase))
            {
                return ProvideNuGetCompletionsAsync(context, pathThroughLastSlash);
            }

            if (string.IsNullOrEmpty(pathThroughLastSlash))
            {
                context.AddItem(CreateNuGetRoot());
            }

            return base.ProvideCompletionsAsync(context, pathThroughLastSlash);
        }

        private async Task ProvideNuGetCompletionsAsync(CompletionContext context, string packageIdAndVersion)
        {
            var (id, version) = ReferenceDirectiveHelper.ParseNuGetReference(packageIdAndVersion);
            var packages = await Task.Run(() => _nuGetCompletionProvider.SearchPackagesAsync(id, version != null, context.CancellationToken),
                context.CancellationToken).ConfigureAwait(false);

            if (version != null)
            {
                if (packages.Count > 0)
                {
                    var package = packages[0];
                    var versions = package.Versions;
                    if (!string.IsNullOrWhiteSpace(version))
                    {
                        versions = versions.Where(v => v.StartsWith(version, StringComparison.InvariantCultureIgnoreCase));
                    }

                    context.AddItems(versions.Select((v, i) =>
                        CommonCompletionItem.Create(
                            v,
                            "",
                            s_rules,
                            Microsoft.CodeAnalysis.Glyph.NuGet,
                            sortText: i.ToString("0000"))));
                }
            }
            else
            {
                context.AddItems(packages.Select((p, i) =>
                    CommonCompletionItem.Create(
                        $"{ReferenceDirectiveHelper.NuGetPrefix} {p.Id}, ",
                        "",
                        s_rules,
                        Microsoft.CodeAnalysis.Glyph.NuGet,
                        sortText: i.ToString("0000"))));
            }
        }

        protected override bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral,
            CancellationToken cancellationToken) =>
            tree.TryGetStringLiteralToken(position, SyntaxKind.ReferenceDirectiveTrivia, out stringLiteral, cancellationToken);
    }

    public interface INuGetCompletionProvider
    {
        Task<IReadOnlyList<INuGetPackage>> SearchPackagesAsync(string searchString, bool exactMatch, CancellationToken cancellationToken);
    }

    public interface INuGetPackage
    {
        string Id { get; }

        IEnumerable<string> Versions { get; }
    }
}
