using ZB.MOM.WW.GalaxyRepository.Grpc;

namespace ZB.MOM.WW.GalaxyRepository;

/// <summary>
///     Maps <see cref="GalaxyHierarchyRow"/> + <see cref="GalaxyAttributeRow"/> rows produced
///     by <see cref="GalaxyRepository"/> into <c>galaxy_repository.v1</c> proto messages.
///     Pure function, separated so it can be unit-tested without a SQL connection.
/// </summary>
public static class GalaxyProtoMapper
{
    /// <summary>Maps Galaxy hierarchy and attribute rows to Galaxy object protos.</summary>
    /// <param name="hierarchy">Hierarchy rows from Galaxy Repository.</param>
    /// <param name="attributes">Attribute rows from Galaxy Repository.</param>
    public static IEnumerable<GalaxyObject> MapHierarchy(
        IReadOnlyList<GalaxyHierarchyRow> hierarchy,
        IReadOnlyList<GalaxyAttributeRow> attributes)
    {
        Dictionary<int, List<GalaxyAttributeRow>> attributesByGobjectId = attributes
            .GroupBy(a => a.GobjectId)
            .ToDictionary(g => g.Key, g => g.ToList());

        foreach (GalaxyHierarchyRow row in hierarchy)
        {
            yield return MapObject(row, attributesByGobjectId);
        }
    }

    /// <summary>Maps a Galaxy hierarchy row to a Galaxy object proto.</summary>
    /// <param name="row">Hierarchy row from Galaxy Repository.</param>
    /// <param name="attributesByGobjectId">Attributes indexed by gobject ID.</param>
    public static GalaxyObject MapObject(
        GalaxyHierarchyRow row,
        IReadOnlyDictionary<int, List<GalaxyAttributeRow>> attributesByGobjectId)
    {
        GalaxyObject obj = new()
        {
            GobjectId = row.GobjectId,
            TagName = row.TagName,
            ContainedName = row.ContainedName,
            BrowseName = row.BrowseName,
            ParentGobjectId = row.ParentGobjectId,
            IsArea = row.IsArea,
            CategoryId = row.CategoryId,
            HostedByGobjectId = row.HostedByGobjectId,
        };
        obj.TemplateChain.AddRange(row.TemplateChain);

        if (attributesByGobjectId.TryGetValue(row.GobjectId, out List<GalaxyAttributeRow>? attrs))
        {
            foreach (GalaxyAttributeRow attr in attrs)
            {
                obj.Attributes.Add(MapAttribute(attr));
            }
        }

        return obj;
    }

    /// <summary>Maps a Galaxy attribute row to a Galaxy attribute proto.</summary>
    /// <param name="row">Attribute row from Galaxy Repository.</param>
    public static GalaxyAttribute MapAttribute(GalaxyAttributeRow row) => new()
    {
        AttributeName = row.AttributeName,
        FullTagReference = row.FullTagReference,
        MxDataType = row.MxDataType,
        DataTypeName = row.DataTypeName ?? string.Empty,
        IsArray = row.IsArray,
        ArrayDimension = row.ArrayDimension ?? 0,
        ArrayDimensionPresent = row.ArrayDimension.HasValue,
        MxAttributeCategory = row.MxAttributeCategory,
        SecurityClassification = row.SecurityClassification,
        IsHistorized = row.IsHistorized,
        IsAlarm = row.IsAlarm,
    };
}
