using Health.Application.Medications;

namespace Health.Infrastructure.Medications;

public sealed class EfMedicationRepository(AppDbContext db) : IMedicationRepository
{
    private readonly AppDbContext _db = db;

    public async Task<IReadOnlyList<Medication>> ListAsync(Guid userId, string? filter, CancellationToken ct)
    {
        var query = _db.Medications.Include(m => m.Logs).Where(m => m.UserId == userId);
        if (filter == "active") query = query.Where(m => m.IsActive);
        else if (filter == "inactive") query = query.Where(m => !m.IsActive);

        return await query.OrderByDescending(m => m.CreatedAt).ToListAsync(ct);
    }

    public async Task<IReadOnlyList<Medication>> ListActiveForRemindersAsync(Guid userId, DateOnly today, CancellationToken ct) =>
        await _db.Medications
            .Where(m => m.UserId == userId && m.IsActive && m.TimeOfDay.Count > 0
                && (m.StartDate == null || m.StartDate <= today)
                && (m.EndDate == null || m.EndDate >= today))
            .ToListAsync(ct);

    public async Task<IReadOnlyList<MedicationLog>> ListLogsInWindowAsync(Guid userId, DateTime startUtc, DateTime endUtc, CancellationToken ct) =>
        await _db.MedicationLogs
            .Where(l => l.UserId == userId && l.ConfirmedAt >= startUtc && l.ConfirmedAt < endUtc)
            .ToListAsync(ct);

    public Task<Medication?> GetOwnedAsync(Guid userId, Guid medicationId, CancellationToken ct) =>
        _db.Medications.FirstOrDefaultAsync(m => m.Id == medicationId && m.UserId == userId, ct);

    public Task<bool> ExistsOwnedAsync(Guid userId, Guid medicationId, CancellationToken ct) =>
        _db.Medications.AnyAsync(m => m.Id == medicationId && m.UserId == userId, ct);

    public Task<MedicationLog?> GetTodayTakenLogAsync(Guid userId, Guid medicationId, DateTime startUtc, DateTime endUtc, CancellationToken ct) =>
        _db.MedicationLogs.FirstOrDefaultAsync(l =>
            l.MedicationId == medicationId && l.UserId == userId
            && l.CreatedAt >= startUtc && l.CreatedAt < endUtc
            && l.Status == MedicationLogStatus.Taken, ct);

    public Task<bool> DoseLogExistsAsync(Guid userId, Guid medicationId, TimeOnly scheduledTime, DateTime startUtc, DateTime endUtc, CancellationToken ct) =>
        _db.MedicationLogs.AnyAsync(l =>
            l.MedicationId == medicationId && l.UserId == userId
            && l.ScheduledTime == scheduledTime
            && l.ConfirmedAt >= startUtc && l.ConfirmedAt < endUtc, ct);

    public Task<MedicationLog?> GetDoseLogAsync(Guid userId, Guid medicationId, TimeOnly scheduledTime, DateTime startUtc, DateTime endUtc, CancellationToken ct) =>
        _db.MedicationLogs.FirstOrDefaultAsync(l =>
            l.MedicationId == medicationId && l.UserId == userId
            && l.ScheduledTime == scheduledTime
            && l.ConfirmedAt >= startUtc && l.ConfirmedAt < endUtc, ct);

    public async Task<IReadOnlyList<Medication>> ListActiveForReminderScanAsync(CancellationToken ct) =>
        await _db.Medications
            .Where(m => m.IsActive && m.TimeOfDay.Count > 0)
            .ToListAsync(ct);

    public async Task AddMedicationAsync(Medication medication, CancellationToken ct) =>
        await _db.Medications.AddAsync(medication, ct);

    public async Task AddLogAsync(MedicationLog log, CancellationToken ct) =>
        await _db.MedicationLogs.AddAsync(log, ct);

    public void DeleteMedication(Medication medication) =>
        _db.Medications.Remove(medication);

    public void DeleteLog(MedicationLog log) =>
        _db.MedicationLogs.Remove(log);

    public Task SaveChangesAsync(CancellationToken ct) =>
        _db.SaveChangesAsync(ct);
}
