using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests
{
    /// <summary>
    ///     Exercises <see cref="AvevaPrerequisites"/> against the live dev box so the helper
    ///     itself gets integration coverage — i.e. "do the probes return Pass for things that
    ///     really are Pass?" as validated against this machine's known-installed topology.
    ///     Category <c>LiveGalaxy</c> so CI / clean dev boxes skip cleanly.
    /// </summary>
    [Trait("Category", "LiveGalaxy")]
    public sealed class AvevaPrerequisitesLiveTests
    {
        private readonly ITestOutputHelper _output;

        public AvevaPrerequisitesLiveTests(ITestOutputHelper output) => _output = output;

        [Fact]
        public async Task CheckAll_on_live_box_reports_Framework_install()
        {
            var report = await AvevaPrerequisites.CheckAllAsync();
            _output.WriteLine(report.ToString());
            report.Checks.ShouldContain(c =>
                c.Name == "registry:ArchestrA.Framework" && c.Status == PrerequisiteStatus.Pass,
                "ArchestrA Framework registry root should be found on this machine.");
        }

        [Fact]
        public async Task CheckAll_on_live_box_reports_aaBootstrap_running()
        {
            var report = await AvevaPrerequisites.CheckAllAsync();
            var bootstrap = report.Checks.FirstOrDefault(c => c.Name == "service:aaBootstrap");
            bootstrap.ShouldNotBeNull();
            bootstrap.Status.ShouldBe(PrerequisiteStatus.Pass,
                $"aaBootstrap must be Running for any live-Galaxy test to work — detail: {bootstrap.Detail}");
        }

        [Fact]
        public async Task CheckAll_on_live_box_reports_aaGR_running()
        {
            var report = await AvevaPrerequisites.CheckAllAsync();
            var gr = report.Checks.FirstOrDefault(c => c.Name == "service:aaGR");
            gr.ShouldNotBeNull();
            gr.Status.ShouldBe(PrerequisiteStatus.Pass,
                $"aaGR (Galaxy Repository) must be Running — detail: {gr.Detail}");
        }

        [Fact]
        public async Task CheckAll_on_live_box_reports_MxAccess_COM_registered()
        {
            var report = await AvevaPrerequisites.CheckAllAsync();
            var com = report.Checks.FirstOrDefault(c => c.Name == "com:LMXProxy");
            com.ShouldNotBeNull();
            com.Status.ShouldBe(PrerequisiteStatus.Pass,
                $"LMXProxy.LMXProxyServer ProgID must resolve to an InprocServer32 DLL — detail: {com.Detail}");
        }

        [Fact]
        public async Task CheckRepositoryOnly_on_live_box_reports_ZB_reachable()
        {
            var report = await AvevaPrerequisites.CheckRepositoryOnlyAsync(ct: CancellationToken.None);
            var zb = report.Checks.FirstOrDefault(c => c.Name == "sql:ZB");
            zb.ShouldNotBeNull();
            zb.Status.ShouldBe(PrerequisiteStatus.Pass,
                $"ZB database must be reachable via SQL Server Windows auth — detail: {zb.Detail}");
        }

        [Fact]
        public async Task CheckRepositoryOnly_on_live_box_reports_non_zero_deployed_objects()
        {
            // This box has 49 deployed objects per the research; we just assert > 0 so adding/
            // removing objects doesn't break the test.
            var report = await AvevaPrerequisites.CheckRepositoryOnlyAsync();
            var deployed = report.Checks.FirstOrDefault(c => c.Name == "sql:ZB.deployedObjects");
            deployed.ShouldNotBeNull();
            deployed.Status.ShouldBe(PrerequisiteStatus.Pass,
                $"At least one deployed gobject should exist — detail: {deployed.Detail}");
        }

        [Fact]
        public async Task Aveva_side_is_ready_on_this_machine()
        {
            // Narrower than "livetest ready" — our own services (OtOpcUa / OtOpcUaGalaxyHost)
            // may not be installed on a developer's box while they're actively iterating on
            // them, but the AVEVA side (Framework / Galaxy Repository / MXAccess COM /
            // SQL / core services) should always be up on a machine with System Platform
            // installed. This assertion is what gates live-Galaxy tests that go straight to
            // the Galaxy Repository without routing through our stack.
            var report = await AvevaPrerequisites.CheckAllAsync(
                new AvevaPrerequisites.Options { CheckGalaxyHostPipe = false });
            _output.WriteLine(report.ToString());
            _output.WriteLine(report.Warnings ?? "no warnings");

            // Enumerate AVEVA-side failures (if any) for an actionable assertion message.
            var avevaFails = report.Checks
                .Where(c => c.Status == PrerequisiteStatus.Fail &&
                            c.Category != PrerequisiteCategory.OtOpcUaService)
                .ToList();
            report.IsAvevaSideReady.ShouldBeTrue(
                avevaFails.Count == 0
                    ? "unexpected state"
                    : "AVEVA-side failures: " + string.Join(" ; ",
                        avevaFails.Select(f => $"{f.Name}: {f.Detail}")));
        }

        [Fact]
        public async Task Report_captures_OtOpcUa_services_state_even_when_not_installed()
        {
            // The helper reports the status of OtOpcUaGalaxyHost + OtOpcUa services even if
            // they're not installed yet — absence is itself an actionable signal. This test
            // doesn't assert Pass/Fail on those services (their state depends on what's
            // installed when the test runs) — it only asserts the helper EMITTED the rows,
            // so nobody can ship a prerequisite check that silently omits our own services.
            var report = await AvevaPrerequisites.CheckAllAsync();

            report.Checks.ShouldContain(c => c.Name == "service:OtOpcUaGalaxyHost");
            report.Checks.ShouldContain(c => c.Name == "service:OtOpcUa");
            report.Checks.ShouldContain(c => c.Name == "service:GLAuth");
        }
    }
}
