using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

[Collection("Integration")]
public class MedicationServiceTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;
    private Guid _patientId;
    private Guid _activeEncounterId;
    private Guid _dischargedEncounterId;

    public MedicationServiceTests(ApiFixture fixture) => _fixture = fixture;

    public async Task InitializeAsync() => await ResetAndSeedAsync();

    public Task DisposeAsync() => Task.CompletedTask;

    private async Task ResetAndSeedAsync()
    {
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await DbResetHelper.ResetAsync(db);

        var patient = new Patient
        {
            Id = Guid.NewGuid(), Mrn = "MRN-MED-001", FirstName = "Med", LastName = "Test",
            DateOfBirth = new DateOnly(1980, 3, 15), Gender = "F", CreatedAt = DateTimeOffset.UtcNow
        };
        var activeEncounter = new Encounter
        {
            Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient,
            Status = EncounterStatus.Active, Department = Department.Icu,
            AttendingPhysician = "Dr. Medication", AdmittedAt = DateTimeOffset.UtcNow,
            CreatedAt = DateTimeOffset.UtcNow
        };
        var dischargedEncounter = new Encounter
        {
            Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient,
            Status = EncounterStatus.Discharged, Department = Department.Icu,
            AttendingPhysician = "Dr. Medication", AdmittedAt = DateTimeOffset.UtcNow.AddDays(-3),
            DischargedAt = DateTimeOffset.UtcNow.AddHours(-1),
            CreatedAt = DateTimeOffset.UtcNow
        };
        db.Patients.Add(patient);
        db.Encounters.Add(activeEncounter);
        db.Encounters.Add(dischargedEncounter);
        await db.SaveChangesAsync();

        _patientId = patient.Id;
        _activeEncounterId = activeEncounter.Id;
        _dischargedEncounterId = dischargedEncounter.Id;
    }

    [Fact]
    public async Task Create_OnActiveEncounter_Succeeds()
    {
        await ResetAndSeedAsync();

        using var scope = _fixture.Services.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<IMedicationService>();

        var med = await service.CreateAsync(_activeEncounterId,
            new CreateMedicationAdministrationRequest(
                "metoprolol", 25m, "mg", "PO", null, "nurse-1"));

        med.Should().NotBeNull();
        med.DrugName.Should().Be("metoprolol");
        med.Dose.Should().Be(25m);
        med.DoseUnit.Should().Be("mg");
        med.Route.Should().Be("PO");
        med.EncounterId.Should().Be(_activeEncounterId);

        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var persisted = await db.MedicationAdministrations.FindAsync(med.Id);
        persisted.Should().NotBeNull();
    }

    [Fact]
    public async Task Create_OnDischargedEncounter_ThrowsConflict()
    {
        await ResetAndSeedAsync();

        using var scope = _fixture.Services.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<IMedicationService>();

        var act = () => service.CreateAsync(_dischargedEncounterId,
            new CreateMedicationAdministrationRequest(
                "metoprolol", 25m, "mg", "PO", null, "nurse-1"));

        await act.Should().ThrowAsync<ConflictException>();
    }

    [Fact]
    public async Task ListByEncounter_ReturnsPaginated()
    {
        await ResetAndSeedAsync();

        using var scope = _fixture.Services.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<IMedicationService>();

        await service.CreateAsync(_activeEncounterId,
            new CreateMedicationAdministrationRequest(
                "metoprolol", 25m, "mg", "PO", null, "nurse-1"));
        await service.CreateAsync(_activeEncounterId,
            new CreateMedicationAdministrationRequest(
                "morphine", 4m, "mg", "IV", null, "nurse-2"));
        await service.CreateAsync(_activeEncounterId,
            new CreateMedicationAdministrationRequest(
                "insulin", 10m, "units", "SubQ", null, "nurse-1"));

        var result = await service.ListByEncounterAsync(_activeEncounterId, null, 1, 2);

        result.TotalCount.Should().Be(3);
        result.Items.Should().HaveCount(2);
        result.TotalPages.Should().Be(2);
    }

    [Fact]
    public async Task GetRecentForEncounter_FiltersByWindow()
    {
        await ResetAndSeedAsync();

        using var scope = _fixture.Services.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<IMedicationService>();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        // Recent metoprolol — within 90-min window
        await service.CreateAsync(_activeEncounterId,
            new CreateMedicationAdministrationRequest(
                "metoprolol", 25m, "mg", "PO",
                DateTimeOffset.UtcNow.AddMinutes(-30), "nurse-1"));

        // Old metoprolol — outside 90-min window
        db.MedicationAdministrations.Add(new MedicationAdministration
        {
            Id = Guid.NewGuid(),
            EncounterId = _activeEncounterId,
            DrugName = "metoprolol",
            Dose = 50m,
            DoseUnit = "mg",
            Route = "PO",
            AdministeredAt = DateTimeOffset.UtcNow.AddMinutes(-120),
            AdministeredBy = "nurse-2"
        });
        await db.SaveChangesAsync();

        var recent = await service.GetRecentForEncounterAsync(
            _activeEncounterId, "SYSTOLIC_BP", 90);

        recent.Should().HaveCount(1);
        recent[0].Dose.Should().Be(25m);
    }
}
