using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;

/// <summary>
/// Default <see cref="IConnectionHealthQueryService"/> implementation.
/// </summary>
/// <remarks>
/// Pure projection — no new health plumbing. The
/// <see cref="ICentralHealthAggregator"/> already holds the latest
/// <c>SiteHealthReport</c> per site (fed by the existing site→central health
/// transport); this service reads those reports and resolves their connection
/// NAME keys back to <c>DataConnection.Id</c> using the same site repository the
/// design page loads from. Health reports key connections by <c>Name</c>, the
/// page renders nodes by id, so the join is per-site Name → Id; doing it per-site
/// avoids cross-site name collisions (two sites can each own a "PLC-1").
/// </remarks>
public sealed class ConnectionHealthQueryService : IConnectionHealthQueryService
{
    private readonly ISiteRepository _siteRepository;
    private readonly ICentralHealthAggregator _healthAggregator;

    /// <summary>
    /// Initializes the service with the site repository (connection name→id source)
    /// and the central health aggregator (latest-report source).
    /// </summary>
    /// <param name="siteRepository">Source of sites and their connections.</param>
    /// <param name="healthAggregator">Source of the latest per-site health report.</param>
    public ConnectionHealthQueryService(
        ISiteRepository siteRepository,
        ICentralHealthAggregator healthAggregator)
    {
        _siteRepository = siteRepository ?? throw new ArgumentNullException(nameof(siteRepository));
        _healthAggregator = healthAggregator ?? throw new ArgumentNullException(nameof(healthAggregator));
    }

    /// <inheritdoc />
    public async Task<IReadOnlyDictionary<int, ConnectionHealth>> GetConnectionHealthAsync(
        CancellationToken ct = default)
    {
        var sites = await _siteRepository.GetAllSitesAsync(ct);
        var connections = await _siteRepository.GetAllDataConnectionsAsync(ct);

        // Per-site connection name → id. Ordinal: connection names are case-sensitive
        // identifiers and the health report keys are produced from the same names.
        var idByName = connections
            .GroupBy(c => c.SiteId)
            .ToDictionary(
                g => g.Key,
                g => g
                    .GroupBy(c => c.Name, StringComparer.Ordinal)
                    .ToDictionary(n => n.Key, n => n.First().Id, StringComparer.Ordinal));

        var result = new Dictionary<int, ConnectionHealth>();
        foreach (var site in sites)
        {
            // Health reports are keyed by the site's machine identifier.
            var report = _healthAggregator.GetSiteState(site.SiteIdentifier)?.LatestReport;
            if (report is null)
            {
                // No report yet (just-started central / heartbeat-only): contribute
                // nothing — the page renders these connections as "unknown".
                continue;
            }

            if (!idByName.TryGetValue(site.Id, out var siteIds))
            {
                continue;
            }

            foreach (var (connName, health) in report.DataConnectionStatuses)
            {
                // Unknown name (e.g. deleted centrally but still live at the site):
                // skip — there is no node to badge.
                if (siteIds.TryGetValue(connName, out var connId))
                {
                    result[connId] = health;
                }
            }
        }

        return result;
    }
}
