/// <summary>
        /// Registers debug symbols with the package database.
        /// </summary>
        /// <param name="packageInfo">Metadata of the package to register.</param>
        /// <param name="database">Database to register to.</param>
        /// <param name="userId">ID of the user uploading the package.</param>
        /// <param name="symbolFileNames">Mapping of symbol id to symbol virtual identifiers.</param>
        /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param>
        /// <returns>Symbol registration result.</returns>
        public async Task <RegisterSymbolResult> RegisterSymbolsAsync(ParsedPackageInfo packageInfo, SlimGetContext database, string userId, IDictionary <SymbolIdentifier, string> symbolFileNames, CancellationToken cancellationToken)
        {
            var pkginfo = packageInfo.Info;
            var pkg     = database.Packages.FirstOrDefault(x => x.IdLowercase == pkginfo.IdLowercase);

            if (pkg == null)
            {
                return(RegisterPackageResult.DoesNotExist);
            }

            if (pkg.OwnerId != userId)
            {
                return(RegisterPackageResult.OwnerMismatch);
            }

            if (pkg.Id != pkginfo.Id)
            {
                return(RegisterPackageResult.IdMismatch);
            }

            var pkgv = database.PackageVersions.FirstOrDefault(x => x.PackageId == pkginfo.Id && x.Version == pkginfo.NormalizedVersion);

            if (pkgv == null)
            {
                return(RegisterPackageResult.DoesNotExist);
            }

            var dbsymbols = database.PackageSymbols
                            .Where(x => x.PackageId == pkginfo.Id && x.PackageVersion == pkginfo.NormalizedVersion)
                            .GroupBy(x => x.Identifier)
                            .ToDictionary(x => x.Key, x => x);

            var regids = new Dictionary <SymbolIdentifier, string>();

            foreach (var bin in packageInfo.Binaries.OfType <ParsedIndexedBinarySymbols>())
            {
                var identifier = new SymbolIdentifier(bin.Identifier, bin.Age, bin.Kind);
                if (!dbsymbols.TryGetValue(bin.Identifier, out var dbsymbolg) || !symbolFileNames.TryGetValue(identifier, out var fnsymbol))
                {
                    continue;
                }

                var fx       = bin.Framework.GetFrameworkString();
                var dbsymbol = dbsymbolg.FirstOrDefault(x => x.Framework == fx);
                if (dbsymbol == null)
                {
                    continue;
                }

                if (dbsymbol.Filename != null)
                {
                    return(RegisterPackageResult.DuplicateSymbols);
                }

                regids[identifier] = bin.Entry;
                dbsymbol.Name      = bin.Name;
                dbsymbol.Filename  = fnsymbol;
                database.PackageSymbols.Update(dbsymbol);
            }

            await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

            return(regids);
        }
        /// <summary>
        /// Prunes old packages that exceed the maximum version count.
        /// </summary>
        /// <param name="id">Id of the package to prune.</param>
        /// <param name="maxCount">Maximum number of package versions to retain.</param>
        /// <param name="database">Database to check for registrations.</param>
        /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param>
        /// <returns>Pruned package information objects.</returns>
        public async Task <IEnumerable <PackageInfo> > PrunePackageAsync(string id, int maxCount, SlimGetContext database, CancellationToken cancellationToken)
        {
            var versions = database.PackageVersions.Where(x => x.PackageId == id).OrderByDescending(x => x.NuGetVersion);

            if (versions.Count() <= maxCount)
            {
                return(Enumerable.Empty <PackageInfo>());
            }

            var prunable = versions.Skip(maxCount);
            var pruned   = prunable.Select(x => new PackageInfo(x.PackageId, x.NuGetVersion));

            database.RemoveRange(prunable);
            await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

            return(pruned);
        }
        /// <summary>
        /// Registers a package with the package database.
        /// </summary>
        /// <param name="packageInfo">Metadata of the package to register.</param>
        /// <param name="database">Database to register to.</param>
        /// <param name="userId">ID of the user uploading the package.</param>
        /// <param name="packageFileName">Virtual file ID of the package file.</param>
        /// <param name="manifestFileName">Virtual file ID of the manifest file.</param>
        /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param>
        /// <returns>Registration result.</returns>
        public async Task <RegisterPackageResult> RegisterPackageAsync(ParsedPackageInfo packageInfo, SlimGetContext database, string userId, string packageFileName, string manifestFileName,
                                                                       CancellationToken cancellationToken)
        {
            var pkginfo = packageInfo.Info;
            var pkg     = database.Packages.FirstOrDefault(x => x.IdLowercase == pkginfo.IdLowercase);
            var pkgv    = default(PackageVersion);

            var result = RegisterPackageResult.VersionCreated;

            if (pkg != null)
            {
                if (pkg.OwnerId != userId)
                {
                    return(RegisterPackageResult.OwnerMismatch);
                }

                if (pkg.Id != pkginfo.Id)
                {
                    return(RegisterPackageResult.IdMismatch);
                }

                pkgv = database.PackageVersions.FirstOrDefault(x => x.PackageId == pkginfo.Id && x.Version == pkginfo.NormalizedVersion);
                if (pkgv != null)
                {
                    return(RegisterPackageResult.AlreadyExists);
                }

                // package exists, ownership matches, id matches, version does not exist, update and create

                pkg.Description               = packageInfo.Description;
                pkg.Language                  = packageInfo.Language;
                pkg.MinimumClientVersion      = packageInfo.MinimumClientVersion;
                pkg.RequiresLicenseAcceptance = packageInfo.RequireLicenseAcceptance;
                pkg.Summary        = packageInfo.Summary;
                pkg.Title          = packageInfo.Title;
                pkg.IconUrl        = packageInfo.IconUrl;
                pkg.LicenseUrl     = packageInfo.LicenseUrl;
                pkg.ProjectUrl     = packageInfo.ProjectUrl;
                pkg.RepositoryUrl  = packageInfo.RepositoryUrl;
                pkg.RepositoryType = packageInfo.RepositoryType;
                pkg.SemVerLevel    = packageInfo.SemVerLevel;
                database.Packages.Update(pkg);

                var oldAuthors = database.PackageAuthors.Where(x => x.PackageId == pkginfo.Id);
                database.PackageAuthors.RemoveRange(oldAuthors);

                var oldTags = database.PackageTags.Where(x => x.PackageId == pkginfo.Id);
                database.PackageTags.RemoveRange(oldTags);
            }
            else
            {
                // package does not exist, create it
                result = RegisterPackageResult.PackageCreated;

                pkg = new Package
                {
                    Id                        = pkginfo.Id,
                    IdLowercase               = pkginfo.IdLowercase,
                    Description               = packageInfo.Description,
                    DownloadCount             = 0,
                    Language                  = packageInfo.Language,
                    IsListed                  = true,
                    MinimumClientVersion      = packageInfo.MinimumClientVersion,
                    PublishedAt               = DateTime.UtcNow,
                    RequiresLicenseAcceptance = packageInfo.RequireLicenseAcceptance,
                    Summary                   = packageInfo.Summary,
                    Title                     = packageInfo.Title,
                    IconUrl                   = packageInfo.IconUrl,
                    LicenseUrl                = packageInfo.LicenseUrl,
                    ProjectUrl                = packageInfo.ProjectUrl,
                    RepositoryUrl             = packageInfo.RepositoryUrl,
                    RepositoryType            = packageInfo.RepositoryType,
                    SemVerLevel               = packageInfo.SemVerLevel,
                    OwnerId                   = userId
                };
                await database.Packages.AddAsync(pkg, cancellationToken).ConfigureAwait(false);
            }

            foreach (var author in packageInfo.Authors)
            {
                await database.PackageAuthors.AddAsync(new PackageAuthor
                {
                    PackageId = pkginfo.Id,
                    Name      = author.Trim()
                }, cancellationToken).ConfigureAwait(false);
            }

            foreach (var tag in packageInfo.Tags)
            {
                await database.PackageTags.AddAsync(new PackageTag
                {
                    PackageId = pkginfo.Id,
                    Tag       = tag.Trim()
                }, cancellationToken).ConfigureAwait(false);
            }

            pkgv = new PackageVersion
            {
                PackageId        = pkginfo.Id,
                Version          = pkginfo.NormalizedVersion,
                VersionLowercase = pkginfo.NormalizedVersion.ToLowerInvariant(),
                DownloadCount    = 0,
                IsPrerelase      = packageInfo.IsPrerelase,
                PublishedAt      = DateTime.UtcNow,
                IsListed         = true,
                PackageFilename  = packageFileName,
                ManifestFilename = manifestFileName
            };
            await database.PackageVersions.AddAsync(pkgv, cancellationToken).ConfigureAwait(false);

            foreach (var fx in packageInfo.Frameworks)
            {
                await database.PackageFrameworks.AddAsync(new PackageFramework
                {
                    PackageId      = pkginfo.Id,
                    PackageVersion = pkginfo.NormalizedVersion,
                    Framework      = fx.GetFrameworkString()
                }, cancellationToken).ConfigureAwait(false);
            }

            foreach (var dep in packageInfo.Dependencies)
            {
                await database.PackageDependencies.AddAsync(new PackageDependency
                {
                    PackageId       = pkginfo.Id,
                    PackageVersion  = pkginfo.NormalizedVersion,
                    TargetFramework = dep.Framework.GetFrameworkString(),
                    Id                    = dep.PackageId,
                    MaxVersion            = dep.MaxVersion?.ToNormalizedString(),
                    MinVersion            = dep.MinVersion?.ToNormalizedString(),
                    IsMaxVersionInclusive = dep.IsMaxInclusive,
                    IsMinVersionInclusive = dep.IsMinInclusive
                }, cancellationToken).ConfigureAwait(false);
            }

            foreach (var bin in packageInfo.Binaries.OfType <ParsedIndexedBinaryExecutable>())
            {
                await database.PackageBinaries.AddAsync(new PackageBinary
                {
                    PackageId      = pkginfo.Id,
                    PackageVersion = pkginfo.NormalizedVersion,
                    Framework      = bin.Framework.GetFrameworkString(),
                    Name           = bin.Entry,
                    Length         = bin.Length,
                    Hash           = bin.Sha256
                }, cancellationToken).ConfigureAwait(false);

                foreach (var symbolId in bin.SymbolIdentifiers)
                {
                    await database.PackageSymbols.AddAsync(new PackageSymbols
                    {
                        PackageId      = pkginfo.Id,
                        PackageVersion = pkginfo.NormalizedVersion,
                        Framework      = bin.Framework.GetFrameworkString(),
                        BinaryName     = bin.Entry,
                        Identifier     = symbolId.Identifier,
                        Age            = symbolId.Age,
                        Kind           = symbolId.Kind,
                        Signature      = $"{symbolId.Identifier.ToString("N").ToUpperInvariant()}{symbolId.Age:x}"
                    }, cancellationToken).ConfigureAwait(false);
                }
            }

            await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

            return(result);
        }