using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;

/// <summary>
/// Shared in-memory fixture for <c>RawTreeService</c> tests. Seeds one enterprise ("zb") with a single
/// cluster ("MAIN") carrying a small but complete Raw-tree slice:
/// a root folder → driver → default device → a nested tag-group with two tags (one historized), plus a
/// cluster-root driver whose device has a device-root tag referenced by a UNS equipment.
/// </summary>
internal static class RawTreeTestDb
{
    public const string ClusterId = "MAIN";
    public const string RootFolderId = "RF-root";
    public const string FolderedDriverId = "DRV-foldered";
    public const string FolderedDeviceId = "DEV-foldered";
    public const string RootGroupId = "TG-root";
    public const string HistorizedTagId = "TAG-hist";
    public const string PlainTagId = "TAG-plain";

    public const string RootDriverId = "DRV-root";
    public const string RootDeviceId = "DEV-root";
    public const string ReferencedTagId = "TAG-referenced";

    public const string EquipmentId = "EQ-000000000001";
    public const string EquipmentName = "packer-1";

    /// <summary>Creates a context bound to the supplied InMemory database name.</summary>
    public static OtOpcUaConfigDbContext CreateNamed(string name) =>
        new(new DbContextOptionsBuilder<OtOpcUaConfigDbContext>()
            .UseInMemoryDatabase(name)
            .Options);

    /// <summary>An <see cref="IDbContextFactory{TContext}"/> whose contexts share one InMemory database.</summary>
    public static IDbContextFactory<OtOpcUaConfigDbContext> Factory(string name) => new NamedFactory(name);

    /// <summary>Seeds the fixture into a fresh, uniquely-named InMemory database and returns its name.</summary>
    public static string SeedFresh()
    {
        var name = $"raw-{Guid.NewGuid():N}";
        using var db = CreateNamed(name);
        Seed(db);
        return name;
    }

    /// <summary>Seeds the fixture into the supplied (already-bound) context and saves.</summary>
    public static void Seed(OtOpcUaConfigDbContext db)
    {
        db.ServerClusters.Add(new ServerCluster
        {
            ClusterId = ClusterId,
            Name = "Main",
            Enterprise = "zb",
            Site = "warsaw-west",
            RedundancyMode = RedundancyMode.None,
            CreatedBy = "test",
        });

        // Foldered branch: folder → driver → device → group → two tags (one historized).
        db.RawFolders.Add(new RawFolder
        {
            RawFolderId = RootFolderId,
            ClusterId = ClusterId,
            ParentRawFolderId = null,
            Name = "PLCs",
        });
        db.DriverInstances.Add(new DriverInstance
        {
            DriverInstanceId = FolderedDriverId,
            ClusterId = ClusterId,
            RawFolderId = RootFolderId,
            Name = "modbus-1",
            DriverType = "Modbus",
            DriverConfig = "{}",
        });
        db.Devices.Add(new Device
        {
            DeviceId = FolderedDeviceId,
            DriverInstanceId = FolderedDriverId,
            Name = "Device1",
            DeviceConfig = "{}",
        });
        db.TagGroups.Add(new TagGroup
        {
            TagGroupId = RootGroupId,
            DeviceId = FolderedDeviceId,
            ParentTagGroupId = null,
            Name = "Motors",
        });
        db.Tags.Add(new Tag
        {
            TagId = HistorizedTagId,
            DeviceId = FolderedDeviceId,
            TagGroupId = RootGroupId,
            Name = "speed",
            DataType = "Float",
            AccessLevel = TagAccessLevel.ReadWrite,
            TagConfig = "{\"isHistorized\":true}",
        });
        db.Tags.Add(new Tag
        {
            TagId = PlainTagId,
            DeviceId = FolderedDeviceId,
            TagGroupId = RootGroupId,
            Name = "state",
            DataType = "Boolean",
            AccessLevel = TagAccessLevel.Read,
            TagConfig = "{}",
        });

        // Cluster-root branch: driver → device → device-root tag referenced by a UNS equipment.
        db.DriverInstances.Add(new DriverInstance
        {
            DriverInstanceId = RootDriverId,
            ClusterId = ClusterId,
            RawFolderId = null,
            Name = "s7-1",
            DriverType = "S7",
            DriverConfig = "{}",
        });
        db.Devices.Add(new Device
        {
            DeviceId = RootDeviceId,
            DriverInstanceId = RootDriverId,
            Name = "Device1",
            DeviceConfig = "{}",
        });
        db.Tags.Add(new Tag
        {
            TagId = ReferencedTagId,
            DeviceId = RootDeviceId,
            TagGroupId = null,
            Name = "temperature",
            DataType = "Float",
            AccessLevel = TagAccessLevel.Read,
            TagConfig = "{}",
        });

        // A UNS equipment referencing the cluster-root tag (delete-block + rename-warning source).
        db.Equipment.Add(new Equipment
        {
            EquipmentId = EquipmentId,
            EquipmentUuid = Guid.NewGuid(),
            UnsLineId = "LINE-1",
            Name = EquipmentName,
            MachineCode = "packer_001",
        });
        db.UnsTagReferences.Add(new UnsTagReference
        {
            UnsTagReferenceId = "REF-1",
            EquipmentId = EquipmentId,
            TagId = ReferencedTagId,
        });

        db.SaveChanges();
    }

    private sealed class NamedFactory(string name) : IDbContextFactory<OtOpcUaConfigDbContext>
    {
        public OtOpcUaConfigDbContext CreateDbContext() => CreateNamed(name);

        public Task<OtOpcUaConfigDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default) =>
            Task.FromResult(CreateNamed(name));
    }
}
