using System;
using StellaOps.Feedser.Storage.Mongo.Exporting;

namespace StellaOps.Feedser.Exporter.TrivyDb;

using System;
using System.Collections.Generic;
using System.Linq;
using StellaOps.Feedser.Storage.Mongo.Exporting;

public sealed class TrivyDbExportPlanner
{
    public TrivyDbExportPlan CreatePlan(
        ExportStateRecord? existingState,
        string treeDigest,
        IReadOnlyList<ExportFileRecord> manifest)
    {
        ArgumentException.ThrowIfNullOrEmpty(treeDigest);
        manifest ??= Array.Empty<ExportFileRecord>();

        if (existingState is null || (existingState.Files?.Count ?? 0) == 0)
        {
            return new TrivyDbExportPlan(
                TrivyDbExportMode.Full,
                treeDigest,
                BaseExportId: existingState?.BaseExportId,
                BaseManifestDigest: existingState?.LastFullDigest,
                ResetBaseline: true,
                Manifest: manifest,
                ChangedFiles: manifest,
                RemovedPaths: Array.Empty<string>());
        }

        var existingFiles = existingState.Files ?? Array.Empty<ExportFileRecord>();
        var cursorMatches = string.Equals(existingState.ExportCursor, treeDigest, StringComparison.Ordinal);
        if (cursorMatches)
        {
            return new TrivyDbExportPlan(
                TrivyDbExportMode.Skip,
                treeDigest,
                existingState.BaseExportId,
                existingState.LastFullDigest,
                ResetBaseline: false,
                Manifest: existingFiles,
                ChangedFiles: Array.Empty<ExportFileRecord>(),
                RemovedPaths: Array.Empty<string>());
        }

        var existingMap = existingFiles.ToDictionary(static file => file.Path, StringComparer.OrdinalIgnoreCase);
        var newMap = manifest.ToDictionary(static file => file.Path, StringComparer.OrdinalIgnoreCase);

        var removed = existingMap.Keys
            .Where(path => !newMap.ContainsKey(path))
            .ToArray();

        if (removed.Length > 0)
        {
            return new TrivyDbExportPlan(
                TrivyDbExportMode.Full,
                treeDigest,
                existingState.BaseExportId,
                existingState.LastFullDigest,
                ResetBaseline: true,
                Manifest: manifest,
                ChangedFiles: manifest,
                RemovedPaths: removed);
        }

        var changed = new List<ExportFileRecord>();
        foreach (var file in manifest)
        {
            if (!existingMap.TryGetValue(file.Path, out var previous) || !string.Equals(previous.Digest, file.Digest, StringComparison.Ordinal))
            {
                changed.Add(file);
            }
        }

        if (changed.Count == 0)
        {
            return new TrivyDbExportPlan(
                TrivyDbExportMode.Skip,
                treeDigest,
                existingState.BaseExportId,
                existingState.LastFullDigest,
                ResetBaseline: false,
                Manifest: existingFiles,
                ChangedFiles: Array.Empty<ExportFileRecord>(),
                RemovedPaths: Array.Empty<string>());
        }

        var hasOutstandingDelta = existingState.LastDeltaDigest is not null;
        if (hasOutstandingDelta)
        {
            return new TrivyDbExportPlan(
                TrivyDbExportMode.Full,
                treeDigest,
                existingState.BaseExportId,
                existingState.LastFullDigest,
                ResetBaseline: true,
                Manifest: manifest,
                ChangedFiles: manifest,
                RemovedPaths: Array.Empty<string>());
        }

        return new TrivyDbExportPlan(
            TrivyDbExportMode.Delta,
            treeDigest,
            existingState.BaseExportId,
            existingState.LastFullDigest,
            ResetBaseline: false,
            Manifest: manifest,
            ChangedFiles: changed,
            RemovedPaths: Array.Empty<string>());
    }
}
