using Dapper;
using Microsoft.EntityFrameworkCore;
using Npgsql;

public class OperationsService : IOperationsService
{
    private readonly AppDbContext _db;
    private readonly IClinicalSyncService _sync;

    public OperationsService(AppDbContext db, IClinicalSyncService sync)
    {
        _db = db;
        _sync = sync;
    }

    public async Task<IReadOnlyList<GatewayFleetItem>> GetGatewayFleetAsync(
        GatewayFleetFilter filter, CancellationToken ct)
    {
        var query = _db.WardGateways
            .AsNoTracking()
            .Include(g => g.Site)
            .AsQueryable();

        if (!string.IsNullOrEmpty(filter.Status))
        {
            var status = GatewayStatusExtensions.FromDbString(filter.Status);
            query = query.Where(g => g.Status == status);
        }

        if (filter.SiteId is Guid siteId)
            query = query.Where(g => g.SiteId == siteId);

        var now = DateTimeOffset.UtcNow;
        return await query
            .OrderByDescending(g => g.Status)
            .ThenByDescending(g => g.LastHeartbeatAt == null)
            .ThenByDescending(g => g.LastHeartbeatAt)
            .Select(g => new GatewayFleetItem(
                g.Id,
                g.GatewayCode,
                g.Department,
                g.Site.SiteCode,
                g.Site.Name,
                g.Status.ToDbString(),
                g.ReportedBufferDepth,
                g.LastHeartbeatAt,
                g.LastSyncAt,
                g.LastHeartbeatAt == null
                    ? null
                    : (double?)(now - g.LastHeartbeatAt.Value).TotalMinutes))
            .ToListAsync(ct);
    }

    public async Task<GatewayDetailResponse> GetGatewayDetailAsync(Guid gatewayId, CancellationToken ct)
    {
        var fleet = await GetGatewayFleetAsync(new GatewayFleetFilter(null, null), ct);
        var gateway = fleet.FirstOrDefault(g => g.Id == gatewayId)
            ?? throw new NotFoundException("Gateway not found.", "GATEWAY_NOT_FOUND");

        var siteId = await _db.WardGateways
            .Where(g => g.Id == gatewayId)
            .Select(g => g.SiteId)
            .FirstAsync(ct);

        var history = await _sync.GetSyncHistoryAsync(siteId, gatewayId, 10, ct);

        var lastConflictBatch = await _db.ClinicalSyncBatches
            .AsNoTracking()
            .Where(b => b.GatewayId == gatewayId && b.Status == ClinicalSyncBatchStatus.Conflict)
            .OrderByDescending(b => b.SubmittedAt)
            .Include(b => b.Conflicts)
            .FirstOrDefaultAsync(ct);

        var pendingConflicts = lastConflictBatch?.Conflicts.Count ?? 0;

        return new GatewayDetailResponse(gateway, history, pendingConflicts);
    }

    public async Task<SiteGatewaySummaryResponse> GetSiteSummaryAsync(Guid siteId, CancellationToken ct)
    {
        var siteExists = await _db.ClinicalSites.AnyAsync(s => s.Id == siteId, ct);
        if (!siteExists)
            throw new NotFoundException("Site not found.", "SITE_NOT_FOUND");

        await using var conn = new NpgsqlConnection(_db.Database.GetConnectionString());
        await conn.OpenAsync(ct);

        const string sql = """
            SELECT
              COUNT(*) AS TotalGateways,
              COUNT(*) FILTER (WHERE status = 'ONLINE') AS Online,
              COUNT(*) FILTER (WHERE status = 'DEGRADED') AS Degraded,
              COUNT(*) FILTER (WHERE status = 'OFFLINE') AS Offline,
              COALESCE(SUM(reported_buffer_depth), 0) AS TotalBufferedEvents
            FROM ward_gateways
            WHERE site_id = @SiteId
            """;

        var row = await conn.QuerySingleAsync(sql, new { SiteId = siteId });

        return new SiteGatewaySummaryResponse(
            siteId,
            (int)row.totalgateways,
            (int)row.online,
            (int)row.degraded,
            (int)row.offline,
            (int)row.totalbufferedevents);
    }
}