using Baya.Application.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.Identity;
using Baya.Domain.Entities.Identity;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal sealed class PatientRepository : BaseAsyncRepository<Patient>, IPatientRepository
{
    public PatientRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task AddAsync(Patient patient, CancellationToken cancellationToken)
        => base.AddAsync(patient);

    public Task<Patient> GetOwnedAsync(long id, long customerId, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(p => p.Id == id && p.CustomerId == customerId, cancellationToken);

    public async Task<PagedResult<PatientDto>> ListAsync(long customerId, int page, int pageSize, CancellationToken cancellationToken)
    {
        var query = TableNoTracking.Where(p => p.CustomerId == customerId);

        var total = await query.CountAsync(cancellationToken);
        var rows = await query
            .OrderByDescending(p => p.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(p => new PatientRow(
                p.Id, p.DisplayName, p.FirstName, p.LastName, p.BirthDate,
                p.Gender, p.BloodType, p.InitialMedicalNotes, p.IsActive, p.Relation, p.ConditionsJson))
            .ToListAsync(cancellationToken);

        var items = rows.Select(ToDto).ToList();
        return new PagedResult<PatientDto>(items, total, page, pageSize);
    }

    public async Task<PatientDto> GetOwnedProjectedAsync(long id, long customerId, CancellationToken cancellationToken)
    {
        var row = await TableNoTracking
            .Where(p => p.Id == id && p.CustomerId == customerId)
            .Select(p => new PatientRow(
                p.Id, p.DisplayName, p.FirstName, p.LastName, p.BirthDate,
                p.Gender, p.BloodType, p.InitialMedicalNotes, p.IsActive, p.Relation, p.ConditionsJson))
            .FirstOrDefaultAsync(cancellationToken);

        return row is null ? null : ToDto(row);
    }

    private static PatientDto ToDto(PatientRow r) => new(
        r.Id, r.DisplayName, r.FirstName, r.LastName, r.BirthDate,
        r.Gender, r.BloodType, r.InitialMedicalNotes, r.IsActive,
        r.Relation, JsonCodeList.Parse(r.ConditionsJson));

    private sealed record PatientRow(
        long Id, string DisplayName, string FirstName, string LastName, DateOnly BirthDate,
        string Gender, string BloodType, string InitialMedicalNotes, bool IsActive,
        string Relation, string ConditionsJson);
}
