#nullable enable
using Baya.Application.Features.PatientCareRecords.Commands.WritePatientCareRecord;
using Baya.Application.Features.PatientCareRecords.Queries.GetPatientHistory;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Reviews;
using Baya.Tests.Setup.Setups;
using Microsoft.EntityFrameworkCore;

namespace Baya.Test.Foundation.Reviews;

public class PatientCareRecordHandlerTests
{
    private const string Note = "Blood pressure 120/80, patient calm, meds taken.";

    [Fact]
    public async Task Nurse_with_confirmed_booking_writes_encrypted_record_and_can_read_it_back()
    {
        using var host = new ReviewsTestHost();
        host.SeedBooking(BookingStatus.Confirmed);

        var write = new WritePatientCareRecordCommandHandler(
            host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock());
        var written = await write.Handle(new WritePatientCareRecordCommand(host.PatientId, null, Note), CancellationToken.None);
        Assert.True(written.IsSuccess);

        // Stored column is ciphertext, not plaintext.
        var stored = host.Db.Set<PatientCareRecord>().AsNoTracking().Single().BodyEncrypted;
        Assert.NotEqual(Note, stored);
        Assert.Equal(Note, TestFieldEncryptor.Instance.Decrypt(stored));

        // The authoring nurse can read the decrypted history.
        var read = await new GetPatientHistoryQueryHandler(host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance)
            .Handle(new GetPatientHistoryQuery(host.PatientId), CancellationToken.None);
        Assert.True(read.IsSuccess);
        var record = Assert.Single(read.Result.Items);
        Assert.Equal(Note, record.Body);
    }

    [Fact]
    public async Task Owning_customer_can_read_history()
    {
        using var host = new ReviewsTestHost();
        host.SeedBooking(BookingStatus.Completed);
        await new WritePatientCareRecordCommandHandler(host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock())
            .Handle(new WritePatientCareRecordCommand(host.PatientId, null, Note), CancellationToken.None);

        var read = await new GetPatientHistoryQueryHandler(host.AsCustomer(), host.UnitOfWork, TestFieldEncryptor.Instance)
            .Handle(new GetPatientHistoryQuery(host.PatientId), CancellationToken.None);

        Assert.True(read.IsSuccess);
        Assert.Equal(Note, Assert.Single(read.Result.Items).Body);
    }

    [Fact]
    public async Task Nurse_without_a_booking_is_denied_write_and_read()
    {
        using var host = new ReviewsTestHost();
        host.SeedBooking(BookingStatus.Completed); // for the assigned nurse only
        await new WritePatientCareRecordCommandHandler(host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock())
            .Handle(new WritePatientCareRecordCommand(host.PatientId, null, Note), CancellationToken.None);

        var write = await new WritePatientCareRecordCommandHandler(host.AsOtherNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock())
            .Handle(new WritePatientCareRecordCommand(host.PatientId, null, "unauthorized"), CancellationToken.None);
        Assert.False(write.IsSuccess);
        Assert.True(write.IsForbidden);

        var read = await new GetPatientHistoryQueryHandler(host.AsOtherNurse(), host.UnitOfWork, TestFieldEncryptor.Instance)
            .Handle(new GetPatientHistoryQuery(host.PatientId), CancellationToken.None);
        Assert.False(read.IsSuccess);
        Assert.True(read.IsForbidden);
    }

    [Fact]
    public async Task Admin_can_read_history()
    {
        using var host = new ReviewsTestHost();
        host.SeedBooking(BookingStatus.Completed);
        await new WritePatientCareRecordCommandHandler(host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock())
            .Handle(new WritePatientCareRecordCommand(host.PatientId, null, Note), CancellationToken.None);

        var read = await new GetPatientHistoryQueryHandler(host.AsAdmin(), host.UnitOfWork, TestFieldEncryptor.Instance)
            .Handle(new GetPatientHistoryQuery(host.PatientId), CancellationToken.None);

        Assert.True(read.IsSuccess);
        Assert.Single(read.Result.Items);
    }

    [Fact]
    public async Task Write_for_missing_patient_is_not_found()
    {
        using var host = new ReviewsTestHost();
        var write = await new WritePatientCareRecordCommandHandler(host.AsNurse(), host.UnitOfWork, TestFieldEncryptor.Instance, host.Clock())
            .Handle(new WritePatientCareRecordCommand(999999, null, Note), CancellationToken.None);

        Assert.False(write.IsSuccess);
        Assert.True(write.IsNotFound);
    }
}
