using System.Globalization;
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;

/// <summary>
/// Maps a gateway wire event (<see cref="HistorianEvent"/>) onto the driver-agnostic
/// <see cref="HistoricalEvent"/> consumed by the Server's HistoryReadEvents path.
/// </summary>
internal static class EventMapper
{
    /// <summary>OPC UA severity range (Part 9): 1 (lowest) … 1000 (highest).</summary>
    private const ushort MinSeverity = 1;
    private const ushort MaxSeverity = 1000;

    /// <summary>Maps a single gateway event to a historical event.</summary>
    /// <param name="historianEvent">The gateway wire event.</param>
    /// <returns>The driver-agnostic historical event.</returns>
    public static HistoricalEvent ToHistoricalEvent(HistorianEvent historianEvent)
    {
        // Message: prefer the "Message" property, else fall back to the event Type (best-effort
        // render); never null-crash on a missing property.
        string? message;
        if (historianEvent.Properties.TryGetValue("Message", out var m) && !string.IsNullOrEmpty(m))
            message = m;
        else
            message = string.IsNullOrEmpty(historianEvent.Type) ? null : historianEvent.Type;

        return new HistoricalEvent(
            EventId: historianEvent.Id,
            SourceName: string.IsNullOrEmpty(historianEvent.SourceName) ? null : historianEvent.SourceName,
            EventTimeUtc: historianEvent.EventTime?.ToDateTime() ?? default,       // Utc kind
            ReceivedTimeUtc: historianEvent.ReceivedTime?.ToDateTime() ?? default, // Utc kind
            Message: message,
            Severity: ParseSeverity(historianEvent.Properties));
    }

    /// <summary>Maps a batch of gateway events to historical events, in order.</summary>
    /// <param name="events">The gateway wire events.</param>
    /// <returns>The driver-agnostic historical events.</returns>
    public static IReadOnlyList<HistoricalEvent> ToHistoricalEvents(IEnumerable<HistorianEvent> events)
    {
        var result = new List<HistoricalEvent>();
        foreach (var historianEvent in events)
            result.Add(ToHistoricalEvent(historianEvent));
        return result;
    }

    /// <summary>
    /// Parses an OPC UA severity from the "Severity" property (else "Priority"), clamped to
    /// <c>[1, 1000]</c>. Missing or unparseable values default to the minimum severity (1).
    /// </summary>
    private static ushort ParseSeverity(IDictionary<string, string> properties)
    {
        string? raw = null;
        if (properties.TryGetValue("Severity", out var severity))
            raw = severity;
        else if (properties.TryGetValue("Priority", out var priority))
            raw = priority;

        if (int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
            return (ushort)Math.Clamp(value, MinSeverity, MaxSeverity);

        return MinSeverity;
    }
}
