using Microsoft.EntityFrameworkCore;

/// <summary>
/// Provides cursor-paginated access to the audit trail of digitization events
/// for a given batch. Events are ordered by occurred_at ascending with id
/// as a tie-breaker for events at the same timestamp.
/// </summary>
public class BatchEventService : IBatchEventService
{
    private readonly AppDbContext _db;
    private const int MaxPageSize = 200;
    private const int DefaultPageSize = 50;

    public BatchEventService(AppDbContext db)
    {
        _db = db;
    }

    public async Task<CursorPagedResult<BatchEventResponse>> GetEventsAsync(
        Guid batchId, DateTimeOffset? after, int pageSize)
    {
        // Validate batch exists
        var batchExists = await _db.DigitizationBatches
            .AnyAsync(b => b.Id == batchId);

        if (!batchExists)
            throw new NotFoundException("Batch not found.", "BATCH_NOT_FOUND");

        // Clamp page size
        pageSize = Math.Clamp(pageSize, 1, MaxPageSize);

        // Build query
        var query = _db.DigitizationEvents
            .AsNoTracking()
            .Include(e => e.Actor)
            .Where(e => e.BatchId == batchId);

        // Apply cursor filter — only events strictly after the cursor timestamp
        if (after.HasValue)
        {
            query = query.Where(e => e.OccurredAt > after.Value);
        }

        // Fetch pageSize+1 rows to determine HasMore without a COUNT query
        var events = await query
            .OrderBy(e => e.OccurredAt)
            .ThenBy(e => e.Id) // tie-breaker for events at the same timestamp
            .Take(pageSize + 1)
            .Select(e => new BatchEventResponse(
                e.Id,
                e.BatchId,
                e.EventType.ToDbString(),
                e.ActorUserId,
                e.Actor.Username,
                e.Actor.FullName,
                e.OccurredAt,
                e.MetadataJson))
            .ToListAsync();

        var hasMore = events.Count > pageSize;
        var page = hasMore ? events.Take(pageSize).ToList() : events;

        // Build next cursor from the last item's OccurredAt
        string? nextCursor = null;
        if (hasMore && page.Count > 0)
        {
            var lastEvent = page[^1];
            // ISO-8601 round-trip format preserves full precision
            nextCursor = lastEvent.OccurredAt.ToString("o");
        }

        return new CursorPagedResult<BatchEventResponse>(
            Items: page,
            PageSize: pageSize,
            NextCursor: nextCursor,
            HasMore: hasMore);
    }
}