#nullable enable
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.Reviews;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Reviews;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal sealed class PatientCareRecordRepository : BaseAsyncRepository<PatientCareRecord>, IPatientCareRecordRepository
{
    // A booking that reached (at least) confirmed ties a nurse to a patient — the clinical-access gate. A
    // pending_payment or cancelled booking never grants clinical access.
    private static readonly string[] QualifyingBookingStatuses =
    [
        BookingStatus.Confirmed, BookingStatus.InProgress, BookingStatus.Completed,
        BookingStatus.Disputed, BookingStatus.Closed
    ];

    public PatientCareRecordRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task AddAsync(PatientCareRecord record, CancellationToken cancellationToken) => base.AddAsync(record);

    public Task<long?> GetPatientOwnerCustomerIdAsync(long patientId, CancellationToken cancellationToken)
        => DbContext.Set<Patient>().AsNoTracking()
            .Where(p => p.Id == patientId)
            .Select(p => (long?)p.CustomerId)
            .FirstOrDefaultAsync(cancellationToken);

    public Task<bool> NurseHasQualifyingBookingForPatientAsync(long nurseProfileId, long patientId, CancellationToken cancellationToken)
        => DbContext.Set<Booking>().AsNoTracking()
            .AnyAsync(b => b.NurseId == nurseProfileId
                           && b.PatientId == patientId
                           && QualifyingBookingStatuses.Contains(b.Status), cancellationToken);

    public async Task<PagedResult<CareRecordCipherRow>> GetPatientHistoryAsync(
        long patientId, int page, int pageSize, CancellationToken cancellationToken)
    {
        var query = Entities.AsNoTracking().Where(r => r.PatientId == patientId);

        var total = await query.CountAsync(cancellationToken);
        var rows = await query
            .OrderByDescending(r => r.RecordedAt)
            .ThenByDescending(r => r.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(r => new
            {
                r.Id, r.PatientId, r.BookingId, r.NurseProfileId, r.BodyEncrypted, r.TaskResultsJson, r.RecordedAt,
                NurseName = DbContext.Set<NurseProfile>()
                    .Where(n => n.Id == r.NurseProfileId)
                    .Select(n => n.User.Name + " " + n.User.FamilyName)
                    .FirstOrDefault()
            })
            .ToListAsync(cancellationToken);

        var items = rows
            .Select(r => new CareRecordCipherRow(
                r.Id, r.PatientId, r.BookingId, r.NurseProfileId,
                string.IsNullOrWhiteSpace(r.NurseName) ? null : r.NurseName.Trim(),
                r.BodyEncrypted, r.TaskResultsJson, r.RecordedAt))
            .ToList();

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

    public Task<PatientCarePlan?> GetCarePlanAsync(long patientId, CancellationToken cancellationToken)
        => DbContext.Set<PatientCarePlan>().FirstOrDefaultAsync(p => p.PatientId == patientId, cancellationToken);

    public async Task AddCarePlanAsync(PatientCarePlan plan, CancellationToken cancellationToken)
        => await DbContext.Set<PatientCarePlan>().AddAsync(plan, cancellationToken);
}
