using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Feedser.Models;
using StellaOps.Feedser.Storage.Mongo.Advisories;

namespace StellaOps.Feedser.Storage.Mongo.Migrations;

public sealed class SemVerStyleBackfillMigration : IMongoMigration
{
    private readonly MongoStorageOptions _options;
    private readonly ILogger<SemVerStyleBackfillMigration> _logger;

    public SemVerStyleBackfillMigration(IOptions<MongoStorageOptions> options, ILogger<SemVerStyleBackfillMigration> logger)
    {
        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public string Id => "20251011-semver-style-backfill";

    public string Description => "Populate advisory.normalizedVersions for existing documents when SemVer style storage is enabled.";

    public async Task ApplyAsync(IMongoDatabase database, CancellationToken cancellationToken)
    {
        if (!_options.EnableSemVerStyle)
        {
            _logger.LogInformation("SemVer style flag disabled; skipping migration {MigrationId}.", Id);
            return;
        }

        var collection = database.GetCollection<AdvisoryDocument>(MongoStorageDefaults.Collections.Advisory);
        var filter = Builders<AdvisoryDocument>.Filter.Or(
            Builders<AdvisoryDocument>.Filter.Exists(doc => doc.NormalizedVersions, false),
            Builders<AdvisoryDocument>.Filter.Where(doc => doc.NormalizedVersions == null || doc.NormalizedVersions.Count == 0));

        var batchSize = Math.Max(25, _options.BackfillBatchSize);
        while (true)
        {
            var pending = await collection.Find(filter)
                .SortBy(doc => doc.AdvisoryKey)
                .Limit(batchSize)
                .ToListAsync(cancellationToken)
                .ConfigureAwait(false);

            if (pending.Count == 0)
            {
                break;
            }

            var updates = new List<WriteModel<AdvisoryDocument>>(pending.Count);
            foreach (var document in pending)
            {
                var advisory = CanonicalJsonSerializer.Deserialize<Advisory>(document.Payload.ToJson());
                var normalized = NormalizedVersionDocumentFactory.Create(advisory);

                if (normalized is null || normalized.Count == 0)
                {
                    updates.Add(new UpdateOneModel<AdvisoryDocument>(
                        Builders<AdvisoryDocument>.Filter.Eq(doc => doc.AdvisoryKey, document.AdvisoryKey),
                        Builders<AdvisoryDocument>.Update.Unset(doc => doc.NormalizedVersions)));
                    continue;
                }

                updates.Add(new UpdateOneModel<AdvisoryDocument>(
                    Builders<AdvisoryDocument>.Filter.Eq(doc => doc.AdvisoryKey, document.AdvisoryKey),
                    Builders<AdvisoryDocument>.Update.Set(doc => doc.NormalizedVersions, normalized)));
            }

            if (updates.Count > 0)
            {
                await collection.BulkWriteAsync(updates, cancellationToken: cancellationToken).ConfigureAwait(false);
            }
        }
    }
}
