using Ethanol.DataObjects;
using System.IO.Compression;
using System.Text.Json;

/// <summary>
/// This class is used to store the malware profiles in a zip file. 
/// </summary>
/// <summary>
/// Represents a profile for malware detection.
/// </summary>
public class MalwareDetectionProfile
{
    record Information(string Version, string[] Families, DateTime Created);

    Information Index { get; init; }
    public MalwareModel[] Models { get; init; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MalwareDetectionProfile"/> class.
    /// </summary>
    /// <param name="models">The malware models to include in the profile.</param>
    public MalwareDetectionProfile(MalwareModel[] models)
    {
        Models = models ?? throw new ArgumentNullException(nameof(models));

        Index = new Information(Version: "1.0", Families: models!.Select(x => x.Family).ToArray(), Created: DateTime.UtcNow);
    }

    /// <summary>
    /// Saves the malware detection profile to the specified path.
    /// </summary>
    /// <param name="path">The path to save the profile.</param>
    public void SaveTo(string path)
    {
        using var zipFile = File.Create(path);
        using (var archive = new ZipArchive(zipFile, ZipArchiveMode.Create))
        {
            var entry = archive.CreateEntry("index.json");
            using (var indexWriter = new StreamWriter(entry.Open()))
            {
                indexWriter.Write(JsonSerializer.Serialize(Index));
                indexWriter.Close();
            }
            foreach (var model in Models)
            {
                var modelEntry = archive.CreateEntry($"{model.Family}.json");
                using var modelWriter = new StreamWriter(modelEntry.Open());
                modelWriter.Write(model.Serialize());
                modelWriter.Close();
            }
        }
    }

    /// <summary>
    /// Loads a malware detection profile from the specified path.
    /// </summary>
    /// <param name="path">The path to load the profile from.</param>
    /// <returns>The loaded malware detection profile.</returns>
    public static MalwareDetectionProfile? LoadFrom(string path)
    {
        using var zipFile = File.OpenRead(path);
        using (var archive = new ZipArchive(zipFile))
        {
            var indexEntry = archive.GetEntry("index.json");
            
            // ERROR missing index file!
            if (indexEntry == null) return null;

            using var indexReader = new StreamReader(indexEntry.Open());
            var index = JsonSerializer.Deserialize<Information>(indexReader.ReadToEnd());
            
            // ERROR reading index!
            if (index == null) return null;

            indexReader.Close();
            var models = new List<MalwareModel>();
            foreach (var family in index.Families.AsSafe())
            {
                var modelEntry = archive.GetEntry($"{family}.json");
                if (modelEntry != null)
                {
                    using var modelReader = new StreamReader(modelEntry.Open());
                    var model = MalwareModel.Deserialize(modelReader.ReadToEnd());
                    if (model != null)
                    {
                        models.Add(model);
                    }
                }
            }
            return new MalwareDetectionProfile(models.ToArray());
        }
    }

    /// <summary>
    /// Gets the malware model associated with the specified family.
    /// </summary>
    /// <param name="family">The family of the malware model.</param>
    /// <returns>The malware model, or null if not found.</returns>
    public MalwareModel? GetModel(string? family)
    {
        return Models.FirstOrDefault(x => x.Family == family);
    }
}

