using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using VigilCare.ClinicalContracts.Sync;

public class ClinicalSyncService : IClinicalSyncService
{
    private readonly AppDbContext _db;
    private readonly ILogger<ClinicalSyncService> _logger;

    public ClinicalSyncService(AppDbContext db, ILogger<ClinicalSyncService> logger)
    {
        _db = db;
        _logger = logger;
    }

    public async Task<ClinicalBatchUploadResponse> UploadBatchAsync(
        ClinicalSyncBatchRequest request, CancellationToken ct)
    {
        var existing = await _db.ClinicalSyncBatches
            .AsNoTracking()
            .FirstOrDefaultAsync(b => b.BatchReference == request.BatchReference, ct);
        if (existing is not null)
            return new ClinicalBatchUploadResponse(existing.Id, existing.Status.ToDbString());

        var gateway = await _db.WardGateways
            .FirstOrDefaultAsync(g => g.Id == request.GatewayId && g.SiteId == request.SiteId, ct)
            ?? throw new NotFoundException("Gateway not found for site.", "GATEWAY_NOT_FOUND");

        var payload = JsonSerializer.Serialize(request);
        var batch = new ClinicalSyncBatch(gateway.Id, request.SiteId, request.BatchReference, payload);
        var batchId = Guid.NewGuid();
        _db.Entry(batch).Property(nameof(ClinicalSyncBatch.Id)).CurrentValue = batchId;

        await using var tx = await _db.Database.BeginTransactionAsync(ct);
        _db.ClinicalSyncBatches.Add(batch);
        _db.OutboxEvents.Add(new OutboxEvent
        {
            Id = Guid.NewGuid(),
            Topic = ClinicalSyncOptions.BatchReceivedOutboxTopic,
            Payload = JsonSerializer.Serialize(new
            {
                batchId,
                gatewayId = gateway.Id,
                siteId = request.SiteId
            }),
            PartitionKey = gateway.Id.ToString(),
            CreatedAt = DateTimeOffset.UtcNow
        });
        await _db.SaveChangesAsync(ct);
        await tx.CommitAsync(ct);

        _logger.LogInformation(
            "Sync batch {BatchId} received from gateway {GatewayId} — {ObsCount} observations",
            batch.Id, gateway.Id, request.Observations.Count);

        return new ClinicalBatchUploadResponse(batch.Id, "RECEIVED");
    }

    public async Task<ClinicalBatchStatusResponse> GetBatchStatusAsync(Guid batchId, CancellationToken ct)
    {
        var batch = await _db.ClinicalSyncBatches
            .AsNoTracking()
            .Include(b => b.Conflicts)
            .FirstOrDefaultAsync(b => b.Id == batchId, ct)
            ?? throw new NotFoundException("Sync batch not found.", "BATCH_NOT_FOUND");

        var conflicts = batch.Conflicts.Select(c =>
            new ClinicalConflictDetail(c.ClientRef, c.ItemType, c.ConflictReason)).ToList();

        return new ClinicalBatchStatusResponse(batch.Id, batch.Status.ToDbString(), conflicts);
    }

    public async Task<IReadOnlyList<ClinicalSyncHistoryItem>> GetSyncHistoryAsync(
        Guid siteId, Guid gatewayId, int limit, CancellationToken ct)
    {
        var gatewayExists = await _db.WardGateways
            .AnyAsync(g => g.Id == gatewayId && g.SiteId == siteId, ct);
        if (!gatewayExists)
            throw new NotFoundException("Gateway not found for site.", "GATEWAY_NOT_FOUND");

        return await _db.ClinicalSyncBatches
            .AsNoTracking()
            .Where(b => b.GatewayId == gatewayId && b.SiteId == siteId)
            .OrderByDescending(b => b.SubmittedAt)
            .Take(limit)
            .Select(b => new ClinicalSyncHistoryItem(
                b.Id,
                b.BatchReference,
                b.Status.ToDbString(),
                b.Conflicts.Count,
                b.SubmittedAt))
            .ToListAsync(ct);
    }
}