using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

public class MedicationService : IMedicationService
{
    private readonly AppDbContext _db;
    private readonly MedicationCorrelationOptions _correlationOptions;

    public MedicationService(
        AppDbContext db,
        IOptions<MedicationCorrelationOptions> correlationOptions)
    {
        _db = db;
        _correlationOptions = correlationOptions.Value;
    }

    public async Task<MedicationAdministration> CreateAsync(
        Guid encounterId, CreateMedicationAdministrationRequest req)
    {
        var encounter = await _db.Encounters.FindAsync(encounterId);
        if (encounter is null)
            throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND");

        if (encounter.Status != EncounterStatus.Active)
            throw new ConflictException(
                "Cannot record medications for a non-active encounter.",
                "ENCOUNTER_NOT_ACTIVE");

        var med = new MedicationAdministration
        {
            Id = Guid.NewGuid(),
            EncounterId = encounterId,
            DrugName = req.DrugName.Trim(),
            Dose = req.Dose,
            DoseUnit = req.DoseUnit.Trim(),
            Route = req.Route.Trim(),
            AdministeredAt = req.AdministeredAt ?? DateTimeOffset.UtcNow,
            AdministeredBy = req.AdministeredBy.Trim()
        };

        _db.MedicationAdministrations.Add(med);
        await _db.SaveChangesAsync();
        return med;
    }

    public async Task<MedicationAdministration> GetByIdAsync(Guid id)
    {
        var med = await _db.MedicationAdministrations
            .AsNoTracking()
            .Include(m => m.Encounter)
            .FirstOrDefaultAsync(m => m.Id == id);

        if (med is null)
            throw new NotFoundException("Medication administration not found.", "MEDICATION_NOT_FOUND");

        return med;
    }

    public async Task<IReadOnlyList<MedicationAdministration>> GetRecentForEncounterAsync(
        Guid encounterId, string observationCode, int windowMinutes)
    {
        var cutoff = DateTimeOffset.UtcNow.AddMinutes(-windowMinutes);
        var mappings = _correlationOptions.DrugVitalMappings;

        // Find drugs that affect this observation code
        var relevantDrugs = mappings
            .Where(kv => kv.Value.Contains(observationCode, StringComparer.OrdinalIgnoreCase))
            .Select(kv => kv.Key)
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        if (relevantDrugs.Count == 0)
            return Array.Empty<MedicationAdministration>();

        return await _db.MedicationAdministrations
            .AsNoTracking()
            .Where(m => m.EncounterId == encounterId
                     && m.AdministeredAt >= cutoff
                     && relevantDrugs.Contains(m.DrugName.ToLower()))
            .OrderByDescending(m => m.AdministeredAt)
            .ToListAsync();
    }

    public async Task<PagedResult<MedicationAdministration>> ListByEncounterAsync(
        Guid encounterId, DateTimeOffset? since, int page, int pageSize)
    {
        var query = _db.MedicationAdministrations
            .AsNoTracking()
            .Where(m => m.EncounterId == encounterId);

        if (since.HasValue)
            query = query.Where(m => m.AdministeredAt >= since.Value);

        var total = await query.CountAsync();
        var items = await query
            .OrderByDescending(m => m.AdministeredAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return new PagedResult<MedicationAdministration>(items, page, pageSize, total);
    }
}