using Microsoft.EntityFrameworkCore;

public class ObservationQueryService : IObservationQueryService
{
    private readonly GatewayDbContext _db;

    public ObservationQueryService(GatewayDbContext db) => _db = db;

    public async Task<CursorPage<LocalObservation>> GetHistoryAsync(
        Guid encounterId,
        string? code,
        DateTimeOffset? from,
        DateTimeOffset? to,
        int limit,
        string? cursorToken)
    {
        limit = Math.Clamp(limit, 1, 100);
        var cursor = ObservationCursor.Decode(cursorToken);

        var query = _db.Observations
            .AsNoTracking()
            .Where(o => o.EncounterId == encounterId);

        if (!string.IsNullOrEmpty(code))
            query = query.Where(o => o.ObservationCode == code);

        if (from.HasValue)
            query = query.Where(o => o.RecordedAt >= from.Value);

        if (to.HasValue)
            query = query.Where(o => o.RecordedAt <= to.Value);

        if (cursor is not null)
        {
            var cursorTime = cursor.RecordedAt;
            var cursorId = cursor.Id;
            query = query.Where(o =>
                o.RecordedAt < cursorTime ||
                (o.RecordedAt == cursorTime && o.Id.CompareTo(cursorId) < 0));
        }

        var items = await query
            .OrderByDescending(o => o.RecordedAt)
            .ThenByDescending(o => o.Id)
            .Take(limit + 1)
            .ToListAsync();

        var hasMore = items.Count > limit;
        if (hasMore) items.RemoveAt(limit);

        var nextCursor = hasMore
            ? new ObservationCursor(items[^1].RecordedAt, items[^1].Id).Encode()
            : null;

        return new CursorPage<LocalObservation>(items, nextCursor, hasMore);
    }
}
