using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Domain;
using ZB.MOM.WW.LmxProxy.Host.Health;
using HealthCheckService = ZB.MOM.WW.LmxProxy.Host.Health.HealthCheckService;
using ZB.MOM.WW.LmxProxy.Host.Metrics;
using ZB.MOM.WW.LmxProxy.Host.Status;
using ZB.MOM.WW.LmxProxy.Host.Subscriptions;

namespace ZB.MOM.WW.LmxProxy.Host.Tests.Status
{
    public class StatusReportServiceTests
    {
        private class FakeScadaClient : IScadaClient
        {
            public bool IsConnected { get; set; } = true;
            public ConnectionState ConnectionState { get; set; } = ConnectionState.Connected;
            public DateTime ConnectedSince => DateTime.UtcNow;
            public int ReconnectCount => 0;
            public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionStateChanged;
            public Task ConnectAsync(CancellationToken ct = default) => Task.CompletedTask;
            public Task DisconnectAsync(CancellationToken ct = default) => Task.CompletedTask;
            public Task<Vtq> ReadAsync(string address, CancellationToken ct = default) =>
                Task.FromResult(Vtq.Good(42.0));
            public Task<IReadOnlyDictionary<string, Vtq>> ReadBatchAsync(IEnumerable<string> addresses, CancellationToken ct = default) =>
                Task.FromResult<IReadOnlyDictionary<string, Vtq>>(new Dictionary<string, Vtq>());
            public Task WriteAsync(string address, object value, CancellationToken ct = default) => Task.CompletedTask;
            public Task WriteBatchAsync(IReadOnlyDictionary<string, object> values, CancellationToken ct = default) => Task.CompletedTask;
            public Task<(bool flagReached, int elapsedMs)> WriteBatchAndWaitAsync(
                IReadOnlyDictionary<string, object> values, string flagTag, object flagValue,
                int timeoutMs, int pollIntervalMs, CancellationToken ct = default) =>
                Task.FromResult((false, 0));
            public Task UnsubscribeByAddressAsync(IEnumerable<string> addresses) => Task.CompletedTask;
            public Task<IAsyncDisposable> SubscribeAsync(IEnumerable<string> addresses, Action<string, Vtq> callback, CancellationToken ct = default) =>
                Task.FromResult<IAsyncDisposable>(new FakeHandle());
            public ValueTask DisposeAsync() => default;
            internal void FireEvent() => ConnectionStateChanged?.Invoke(this, null!);
            private class FakeHandle : IAsyncDisposable { public ValueTask DisposeAsync() => default; }
        }

        private (StatusReportService svc, PerformanceMetrics pm, SubscriptionManager sm) CreateService(
            bool connected = true)
        {
            var client = new FakeScadaClient
            {
                IsConnected = connected,
                ConnectionState = connected ? ConnectionState.Connected : ConnectionState.Disconnected
            };
            var sm = new SubscriptionManager(client);
            var pm = new PerformanceMetrics();
            var health = new HealthCheckService(client, sm, pm);
            var svc = new StatusReportService(client, sm, pm, health);
            return (svc, pm, sm);
        }

        [Fact]
        public async Task GenerateJsonReportAsync_ReturnsCamelCaseJson()
        {
            var (svc, pm, sm) = CreateService();
            using (pm) using (sm)
            {
                var json = await svc.GenerateJsonReportAsync();

                json.Should().Contain("\"serviceName\"");
                json.Should().Contain("\"connection\"");
                json.Should().Contain("\"isConnected\"");
            }
        }

        [Fact]
        public async Task GenerateHtmlReportAsync_ContainsAutoRefresh()
        {
            var (svc, pm, sm) = CreateService();
            using (pm) using (sm)
            {
                var html = await svc.GenerateHtmlReportAsync();

                html.Should().Contain("<meta http-equiv=\"refresh\" content=\"30\">");
            }
        }

        [Fact]
        public async Task IsHealthyAsync_ReturnsTrueWhenHealthy()
        {
            var (svc, pm, sm) = CreateService(connected: true);
            using (pm) using (sm)
            {
                var result = await svc.IsHealthyAsync();

                result.Should().BeTrue();
            }
        }

        [Fact]
        public async Task IsHealthyAsync_ReturnsFalseWhenUnhealthy()
        {
            var (svc, pm, sm) = CreateService(connected: false);
            using (pm) using (sm)
            {
                var result = await svc.IsHealthyAsync();

                result.Should().BeFalse();
            }
        }

        [Fact]
        public async Task GenerateJsonReportAsync_IncludesPerformanceMetrics()
        {
            var (svc, pm, sm) = CreateService();
            using (pm) using (sm)
            {
                pm.RecordOperation("Read", TimeSpan.FromMilliseconds(15), true);
                pm.RecordOperation("Write", TimeSpan.FromMilliseconds(25), true);

                var json = await svc.GenerateJsonReportAsync();
                var parsed = JObject.Parse(json);

                var operations = parsed["performance"]?["operations"];
                operations.Should().NotBeNull();
                // Newtonsoft CamelCasePropertyNamesContractResolver camelCases dictionary keys
                operations!["read"].Should().NotBeNull();
                operations!["write"].Should().NotBeNull();
                ((long)operations!["read"]!["totalCount"]!).Should().Be(1);
            }
        }
    }
}
