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 HistoryReadEventsTests
{
    private static MxAccessGalaxyBackend BuildBackend(IHistorianDataSource? h, StaPump pump) =>
        new(
            new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
            new MxAccessClient(pump, new MxProxyAdapter(), "events-test"),
            h);

    [Fact]
    public async Task Returns_disabled_error_when_no_historian_configured()
    {
        using var pump = new StaPump("Test.Sta");
        await pump.WaitForStartedAsync();
        using var backend = BuildBackend(null, pump);

        var resp = await backend.HistoryReadEventsAsync(new HistoryReadEventsRequest
        {
            SourceName = "TankA",
            StartUtcUnixMs = 0,
            EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            MaxEvents = 100,
        }, CancellationToken.None);

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

    [Fact]
    public async Task Maps_HistorianEventDto_to_GalaxyHistoricalEvent_wire_shape()
    {
        using var pump = new StaPump("Test.Sta");
        await pump.WaitForStartedAsync();

        var eventId = Guid.NewGuid();
        var eventTime = new DateTime(2026, 4, 18, 10, 0, 0, DateTimeKind.Utc);
        var receivedTime = eventTime.AddMilliseconds(150);
        var fake = new FakeHistorian(new HistorianEventDto
        {
            Id = eventId,
            Source = "TankA.Level.HiHi",
            EventTime = eventTime,
            ReceivedTime = receivedTime,
            DisplayText = "HiHi alarm tripped",
            Severity = 900,
        });
        using var backend = BuildBackend(fake, pump);

        var resp = await backend.HistoryReadEventsAsync(new HistoryReadEventsRequest
        {
            SourceName = "TankA.Level.HiHi",
            StartUtcUnixMs = 0,
            EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            MaxEvents = 50,
        }, CancellationToken.None);

        resp.Success.ShouldBeTrue();
        resp.Events.Length.ShouldBe(1);
        var got = resp.Events[0];
        got.EventId.ShouldBe(eventId.ToString());
        got.SourceName.ShouldBe("TankA.Level.HiHi");
        got.DisplayText.ShouldBe("HiHi alarm tripped");
        got.Severity.ShouldBe<ushort>(900);
        got.EventTimeUtcUnixMs.ShouldBe(new DateTimeOffset(eventTime, TimeSpan.Zero).ToUnixTimeMilliseconds());
        got.ReceivedTimeUtcUnixMs.ShouldBe(new DateTimeOffset(receivedTime, TimeSpan.Zero).ToUnixTimeMilliseconds());

        fake.LastSourceName.ShouldBe("TankA.Level.HiHi");
        fake.LastMaxEvents.ShouldBe(50);
    }

    [Fact]
    public async Task Null_source_name_is_passed_through_as_all_sources()
    {
        using var pump = new StaPump("Test.Sta");
        await pump.WaitForStartedAsync();
        var fake = new FakeHistorian();
        using var backend = BuildBackend(fake, pump);

        await backend.HistoryReadEventsAsync(new HistoryReadEventsRequest
        {
            SourceName = null,
            StartUtcUnixMs = 0,
            EndUtcUnixMs = 1,
            MaxEvents = 10,
        }, CancellationToken.None);

        fake.LastSourceName.ShouldBeNull();
    }

    private sealed class FakeHistorian : IHistorianDataSource
    {
        private readonly HistorianEventDto[] _events;
        public string? LastSourceName { get; private set; } = "<unset>";
        public int LastMaxEvents { get; private set; }

        public FakeHistorian(params HistorianEventDto[] events) => _events = events;

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

        public Task<List<HistorianSample>> ReadRawAsync(string tag, DateTime s, DateTime e, int max, CancellationToken ct)
            => Task.FromResult(new List<HistorianSample>());
        public Task<List<HistorianAggregateSample>> ReadAggregateAsync(string tag, DateTime s, DateTime e, double ms, string col, CancellationToken ct)
            => Task.FromResult(new List<HistorianAggregateSample>());
        public Task<List<HistorianSample>> ReadAtTimeAsync(string tag, DateTime[] ts, CancellationToken ct)
            => Task.FromResult(new List<HistorianSample>());
        public HistorianHealthSnapshot GetHealthSnapshot() => new();
        public void Dispose() { }
    }
}
