using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Galaxy;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Historian;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.MxAccess;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Sta;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests
{
    [Trait("Category", "Unit")]
    public sealed class HistorianWiringTests
    {
        /// <summary>
        ///     When the Proxy sends a HistoryRead but the supervisor never enabled the historian
        ///     (OTOPCUA_HISTORIAN_ENABLED unset), we expect a clean Success=false with a
        ///     self-explanatory error — not an exception or a hang against localhost.
        /// </summary>
        [Fact]
        public async Task HistoryReadAsync_returns_disabled_error_when_no_historian_configured()
        {
            using var pump = new StaPump("Test.Sta");
            await pump.WaitForStartedAsync();
            var mx = new MxAccessClient(pump, new MxProxyAdapter(), "HistorianWiringTests");
            using var backend = new MxAccessGalaxyBackend(
                new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
                mx,
                historian: null);

            var resp = await backend.HistoryReadAsync(new HistoryReadRequest
            {
                TagReferences = new[] { "TestTag" },
                StartUtcUnixMs = 0,
                EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                MaxValuesPerTag = 100,
            }, CancellationToken.None);

            resp.Success.ShouldBeFalse();
            resp.Error.ShouldContain("Historian disabled");
            resp.Tags.ShouldBeEmpty();
        }

        /// <summary>
        ///     When the historian is wired up, we expect the backend to call through and map
        ///     samples onto the IPC wire shape. Uses a fake <see cref="IHistorianDataSource"/>
        ///     that returns a single known-good sample so we can assert the mapping stays sane.
        /// </summary>
        [Fact]
        public async Task HistoryReadAsync_maps_sample_to_GalaxyDataValue()
        {
            using var pump = new StaPump("Test.Sta");
            await pump.WaitForStartedAsync();
            var mx = new MxAccessClient(pump, new MxProxyAdapter(), "HistorianWiringTests");
            var fake = new FakeHistorianDataSource(new HistorianSample
            {
                Value = 42.5,
                Quality = 192, // Good
                TimestampUtc = new DateTime(2026, 4, 18, 9, 0, 0, DateTimeKind.Utc),
            });
            using var backend = new MxAccessGalaxyBackend(
                new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
                mx,
                fake);

            var resp = await backend.HistoryReadAsync(new HistoryReadRequest
            {
                TagReferences = new[] { "TankLevel" },
                StartUtcUnixMs = 0,
                EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                MaxValuesPerTag = 100,
            }, CancellationToken.None);

            resp.Success.ShouldBeTrue();
            resp.Tags.Length.ShouldBe(1);
            resp.Tags[0].TagReference.ShouldBe("TankLevel");
            resp.Tags[0].Values.Length.ShouldBe(1);
            resp.Tags[0].Values[0].StatusCode.ShouldBe(0u); // Good
            resp.Tags[0].Values[0].ValueBytes.ShouldNotBeNull();
            resp.Tags[0].Values[0].SourceTimestampUtcUnixMs.ShouldBe(
                new DateTimeOffset(2026, 4, 18, 9, 0, 0, TimeSpan.Zero).ToUnixTimeMilliseconds());
        }

        private sealed class FakeHistorianDataSource : IHistorianDataSource
        {
            private readonly HistorianSample _sample;
            public FakeHistorianDataSource(HistorianSample sample) => _sample = sample;

            public Task<List<HistorianSample>> ReadRawAsync(string tagName, DateTime s, DateTime e, int max, CancellationToken ct)
                => Task.FromResult(new List<HistorianSample> { _sample });

            public Task<List<HistorianAggregateSample>> ReadAggregateAsync(string tagName, DateTime s, DateTime e, double ms, string col, CancellationToken ct)
                => Task.FromResult(new List<HistorianAggregateSample>());

            public Task<List<HistorianSample>> ReadAtTimeAsync(string tagName, DateTime[] ts, CancellationToken ct)
                => Task.FromResult(new List<HistorianSample>());

            public Task<List<HistorianEventDto>> ReadEventsAsync(string? src, DateTime s, DateTime e, int max, CancellationToken ct)
                => Task.FromResult(new List<HistorianEventDto>());

            public HistorianHealthSnapshot GetHealthSnapshot() => new();
            public void Dispose() { }
        }
    }
}
