
using System.IO.Compression;
using Ethanol.MalwareSonar.Fuzzy;
using System.Text.Json;
/// <summary>
/// Represents a model for malware detection.
/// </summary>
public class MalwareModel
{
    /// <summary>
    /// Gets or sets the family of the malware.
    /// </summary>
    public string Family { get; init; } = String.Empty;

    /// <summary>
    /// Gets or sets the fuzzy set of URLs associated with the malware.
    /// </summary>
    public FuzzySet<string> Urls { get; init; }=  FuzzySet<string>.Empty;

    /// <summary>
    /// Gets or sets the fuzzy set of domains associated with the malware.
    /// </summary>
    public FuzzySet<string> Domains { get; init; }= FuzzySet<string>.Empty;

    /// <summary>
    /// Gets or sets the fuzzy set of IP addresses associated with the malware.
    /// </summary>
    public FuzzySet<string> Ips { get; init; } = FuzzySet<string>.Empty;

    /// <summary>
    /// Gets or sets the thresholds for URL matching.
    /// </summary>
    public double[] UrlThresholds { get; init; }= Array.Empty<double>();

    /// <summary>
    /// Gets or sets the thresholds for domain matching.
    /// </summary>
    public double[] DomainThresholds { get; init; }= Array.Empty<double>();

    /// <summary>
    /// Gets or sets the thresholds for IP matching.
    /// </summary>
    public double[] IpsThresholds { get; init; } = Array.Empty<double>();

    /// <summary>
    /// Gets or sets the name of the malware.
    /// </summary>
    public string Name { get; init; }= String.Empty;

    /// <summary>
    /// Gets or sets the description of the malware.
    /// </summary>
    public string Description { get; init; } = String.Empty;

    /// <summary>
    /// Gets or sets the information reference URL for the malware.
    /// </summary>
    public string InformationRefUrl { get; init; }= String.Empty;

    /// <summary>
    /// Deserializes a JSON string into a <see cref="MalwareModel"/> object.
    /// </summary>
    /// <param name="json">The JSON string to deserialize.</param>
    /// <returns>The deserialized <see cref="MalwareModel"/> object.</returns>
    public static MalwareModel? Deserialize(string json)
    {
        var options = new System.Text.Json.JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
        };
        options.Converters.Add(new FuzzySetJsonSerializer<string>());
        return System.Text.Json.JsonSerializer.Deserialize<MalwareModel>(json, options);
    }

    /// <summary>
    /// Serializes the <see cref="MalwareModel"/> object into a JSON string.
    /// </summary>
    /// <returns>The serialized JSON string.</returns>
    public string Serialize()
    {
        var options = new System.Text.Json.JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
        };
        options.Converters.Add(new FuzzySetJsonSerializer<string>());
        return System.Text.Json.JsonSerializer.Serialize(this, options);
    }

    /// <summary>
    /// Calculates the mean and standard deviation of an array of numbers.
    /// </summary>
    /// <param name="numbers">The array of numbers.</param>
    /// <returns>A tuple containing the mean and standard deviation.</returns>
    static MeanAndStdev MeanWithStandardDeviation(double[] numbers)
    {
        var mean = numbers.Average();
        var sumOfSquaresOfDifferences = numbers.Select(val => (val - mean) * (val - mean)).Sum();
        var standardDeviation = Math.Sqrt(sumOfSquaresOfDifferences / numbers.Count());
        return new MeanAndStdev(mean, standardDeviation);
    }

    /// <summary>
    /// Matches the malware model against the given context indicators.
    /// </summary>
    /// <param name="context">The context indicators.</param>
    /// <returns>A <see cref="MalwareMatch"/> object representing the match result.</returns>
    public MalwareMatch Match(ContextIndicators context)
    {
        var urlsFuzzy = FuzzySet<string>.FromEnumerable(context.Urls.Distinct());
        var domainsFuzzy = FuzzySet<string>.FromEnumerable(context.Domains.Where(x => x != null).Distinct());
        var ipsFuzzy = FuzzySet<string>.FromEnumerable(context.Ips.Distinct());

        return new MalwareMatch(
            Host: context.Host,
            Start: context.Start,
            End: context.End,
            Malware: Family,
            Urls: FuzzySetOperations.AndOperation<string>(urlsFuzzy, Urls, FuzzySetOperations.NormFunctions.Hamacher),
            Domains: FuzzySetOperations.AndOperation<string>(domainsFuzzy, Domains, FuzzySetOperations.NormFunctions.Hamacher),
            Ips: FuzzySetOperations.AndOperation<string>(ipsFuzzy, Ips, FuzzySetOperations.NormFunctions.Hamacher),
            MeanWithStandardDeviation(UrlThresholds),
            MeanWithStandardDeviation(DomainThresholds),
            MeanWithStandardDeviation(IpsThresholds)
        );
    }
}

