using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
using StellaOps.Scanner.Core.Utility;

namespace StellaOps.Scanner.Core.Contracts;

[JsonConverter(typeof(JsonStringEnumConverter<ScannerErrorCode>))]
public enum ScannerErrorCode
{
    Unknown = 0,
    InvalidImageReference,
    ImageNotFound,
    AuthorizationFailed,
    QueueUnavailable,
    StorageUnavailable,
    AnalyzerFailure,
    ExportFailure,
    SigningFailure,
    RuntimeFailure,
    Timeout,
    Cancelled,
    PluginViolation
}

[JsonConverter(typeof(JsonStringEnumConverter<ScannerErrorSeverity>))]
public enum ScannerErrorSeverity
{
    Warning = 0,
    Error,
    Fatal
}

public sealed class ScannerError
{
    private static readonly IReadOnlyDictionary<string, string> EmptyDetails =
        new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(0, StringComparer.Ordinal));

    [JsonConstructor]
    public ScannerError(
        ScannerErrorCode code,
        ScannerErrorSeverity severity,
        string message,
        DateTimeOffset timestamp,
        bool retryable,
        IReadOnlyDictionary<string, string>? details = null,
        string? stage = null,
        string? component = null)
    {
        if (string.IsNullOrWhiteSpace(message))
        {
            throw new ArgumentException("Error message cannot be null or whitespace.", nameof(message));
        }

        Code = code;
        Severity = severity;
        Message = message.Trim();
        Timestamp = ScannerTimestamps.Normalize(timestamp);
        Retryable = retryable;
        Stage = stage;
        Component = component;
        Details = details is null or { Count: 0 }
            ? EmptyDetails
            : new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(details, StringComparer.Ordinal));
    }

    [JsonPropertyName("code")]
    [JsonPropertyOrder(0)]
    public ScannerErrorCode Code { get; }

    [JsonPropertyName("severity")]
    [JsonPropertyOrder(1)]
    public ScannerErrorSeverity Severity { get; }

    [JsonPropertyName("message")]
    [JsonPropertyOrder(2)]
    public string Message { get; }

    [JsonPropertyName("timestamp")]
    [JsonPropertyOrder(3)]
    public DateTimeOffset Timestamp { get; }

    [JsonPropertyName("retryable")]
    [JsonPropertyOrder(4)]
    public bool Retryable { get; }

    [JsonPropertyName("stage")]
    [JsonPropertyOrder(5)]
    public string? Stage { get; }

    [JsonPropertyName("component")]
    [JsonPropertyOrder(6)]
    public string? Component { get; }

    [JsonPropertyName("details")]
    [JsonPropertyOrder(7)]
    public IReadOnlyDictionary<string, string> Details { get; }

    public ScannerError WithDetail(string key, string value)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(key);
        ArgumentException.ThrowIfNullOrWhiteSpace(value);

        var mutable = new Dictionary<string, string>(Details, StringComparer.Ordinal)
        {
            [key] = value
        };

        return new ScannerError(Code, Severity, Message, Timestamp, Retryable, mutable, Stage, Component);
    }
}
