using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.TemplateEngine.Flattening;

namespace ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests.Flattening;

public class ConnectionBindingOverrideTests
{
    private readonly FlatteningService _sut = new();

    private static Instance CreateInstance(string name = "TestInstance", int templateId = 1, int siteId = 1) =>
        new(name) { Id = 1, TemplateId = templateId, SiteId = siteId };

    private static Template CreateTemplate(int id, string name)
    {
        return new Template(name) { Id = id };
    }

    private static Template CreateTemplateWithDataSourcedAttribute(string attributeName, string dataSourceReference)
    {
        var template = CreateTemplate(1, "Base");
        template.Attributes.Add(new TemplateAttribute(attributeName)
        {
            DataType = DataType.Double,
            DataSourceReference = dataSourceReference
        });
        return template;
    }

    private static Dictionary<int, DataConnection> SingleConnection(int id = 1) =>
        new()
        {
            [id] = new("OPC-Server1", "OpcUa", 1) { Id = id, PrimaryConfiguration = "opc.tcp://localhost:4840" }
        };

    [Fact]
    public void Override_replaces_template_DataSourceReference_when_set()
    {
        var template = CreateTemplateWithDataSourcedAttribute("Speed", dataSourceReference: "TemplateDefault");
        var instance = CreateInstance();
        instance.ConnectionBindings.Add(new InstanceConnectionBinding("Speed")
        {
            DataConnectionId = 1,
            DataSourceReferenceOverride = "ns=2;s=Pump1.Speed"
        });

        var result = _sut.Flatten(
            instance,
            [template],
            new Dictionary<int, IReadOnlyList<TemplateComposition>>(),
            new Dictionary<int, IReadOnlyList<Template>>(),
            SingleConnection(id: 1));

        Assert.True(result.IsSuccess);
        var attr = result.Value.Attributes.Single(a => a.CanonicalName == "Speed");
        Assert.Equal("ns=2;s=Pump1.Speed", attr.DataSourceReference);
    }

    [Fact]
    public void Null_override_falls_back_to_template_default()
    {
        var template = CreateTemplateWithDataSourcedAttribute("Speed", dataSourceReference: "TemplateDefault");
        var instance = CreateInstance();
        instance.ConnectionBindings.Add(new InstanceConnectionBinding("Speed")
        {
            DataConnectionId = 1,
            DataSourceReferenceOverride = null
        });

        var result = _sut.Flatten(
            instance,
            [template],
            new Dictionary<int, IReadOnlyList<TemplateComposition>>(),
            new Dictionary<int, IReadOnlyList<Template>>(),
            SingleConnection(id: 1));

        Assert.True(result.IsSuccess);
        var attr = result.Value.Attributes.Single(a => a.CanonicalName == "Speed");
        Assert.Equal("TemplateDefault", attr.DataSourceReference);
    }

    [Fact]
    public void Override_change_changes_revision_hash()
    {
        var template = CreateTemplateWithDataSourcedAttribute("Speed", dataSourceReference: "TemplateDefault");

        var instance1 = CreateInstance();
        instance1.ConnectionBindings.Add(new InstanceConnectionBinding("Speed")
        {
            DataConnectionId = 1,
            DataSourceReferenceOverride = "ns=2;s=Pump1.Speed"
        });

        var instance2 = CreateInstance();
        instance2.ConnectionBindings.Add(new InstanceConnectionBinding("Speed")
        {
            DataConnectionId = 1,
            DataSourceReferenceOverride = "ns=2;s=Pump2.Speed"
        });

        var connections = SingleConnection(id: 1);
        var compositionMap = new Dictionary<int, IReadOnlyList<TemplateComposition>>();
        var composedChains = new Dictionary<int, IReadOnlyList<Template>>();

        var result1 = _sut.Flatten(instance1, [template], compositionMap, composedChains, connections);
        var result2 = _sut.Flatten(instance2, [template], compositionMap, composedChains, connections);

        Assert.True(result1.IsSuccess);
        Assert.True(result2.IsSuccess);

        var hasher = new RevisionHashService();
        var hash1 = hasher.ComputeHash(result1.Value);
        var hash2 = hasher.ComputeHash(result2.Value);

        Assert.NotEqual(hash1, hash2);
    }
}
