using System.Globalization;
using Baya.Application.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Identity;
using Baya.Application.Models.Nurses;
using Baya.Domain.Entities.Catalog;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Reviews;
using Baya.Domain.Entities.Verification;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal sealed class NurseProfileRepository : BaseAsyncRepository<NurseProfile>, INurseProfileRepository
{
    public NurseProfileRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task<NurseProfile> GetByUserIdAsync(int userId, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(p => p.UserId == userId, cancellationToken);

    public Task<NurseProfile> GetTrackedByIdAsync(long nurseProfileId, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(p => p.Id == nurseProfileId, cancellationToken);

    public Task AddAsync(NurseProfile profile, CancellationToken cancellationToken)
        => base.AddAsync(profile);

    public Task<NurseProfileDto> GetMineAsync(int userId, CancellationToken cancellationToken)
        => TableNoTracking
            .Where(p => p.UserId == userId)
            .Select(p => new NurseProfileDto(
                p.Id,
                p.Bio,
                p.YearsOfExperience,
                p.EducationLevel,
                p.EducationField,
                p.SpecializationsJson,
                p.IsVerified,
                p.IsAcceptingBookings,
                p.AverageRating,
                p.TotalReviews,
                p.TotalCompletedBookings,
                p.AvatarUrl))
            .FirstOrDefaultAsync(cancellationToken);

    public Task<long?> GetProfileIdByUserIdAsync(int userId, CancellationToken cancellationToken)
        => TableNoTracking
            .Where(p => p.UserId == userId)
            .Select(p => (long?)p.Id)
            .FirstOrDefaultAsync(cancellationToken);

    public Task<NurseIdentityContext> GetIdentityContextByUserIdAsync(int userId, CancellationToken cancellationToken)
        => TableNoTracking
            .Where(p => p.UserId == userId)
            .Select(p => new NurseIdentityContext(p.Id, p.User.NationalId))
            .FirstOrDefaultAsync(cancellationToken);

    public Task<NurseBookingContext> GetBookingContextByIdAsync(long nurseProfileId, CancellationToken cancellationToken)
        => TableNoTracking
            .Where(p => p.Id == nurseProfileId)
            .Select(p => new NurseBookingContext(p.UserId, p.User.Gender, p.IsVerified, p.IsAcceptingBookings))
            .FirstOrDefaultAsync(cancellationToken);

    public async Task<NursePublicProfileDto> GetPublicProfileAsync(long nurseProfileId, CancellationToken cancellationToken)
    {
        var core = await TableNoTracking
            .Where(p => p.Id == nurseProfileId)
            .Select(p => new
            {
                p.Id,
                Name = ((p.User.Name ?? "") + " " + (p.User.FamilyName ?? "")).Trim(),
                p.AvatarUrl,
                p.Bio,
                p.YearsOfExperience,
                p.AverageRating,
                p.TotalReviews,
                p.TotalCompletedBookings,
                p.IsVerified,
                p.SpecializationsJson
            })
            .FirstOrDefaultAsync(cancellationToken);

        if (core is null)
            return null;

        var services = await DbContext.Set<NurseServiceVariant>()
            .AsNoTracking()
            .Where(v => v.NurseId == nurseProfileId && v.IsActive)
            .OrderBy(v => v.Id)
            .Select(v => new { v.Id, v.DisplayName, v.Price, v.PriceUnit, v.SessionCount })
            .ToListAsync(cancellationToken);

        var inoMembership = await DbContext.Set<NurseCredential>()
            .AsNoTracking()
            .AnyAsync(c => c.NurseId == nurseProfileId && c.CredentialType == CredentialTypes.InoMembership, cancellationToken);

        var latest = await DbContext.Set<Review>()
            .AsNoTracking()
            .Where(r => r.NurseProfileId == nurseProfileId && r.ModerationStatus == ReviewModerationStatus.Published)
            .OrderByDescending(r => r.Id)
            .Select(r => new { r.Rating, r.Body, r.CreatedAt })
            .FirstOrDefaultAsync(cancellationToken);

        return new NursePublicProfileDto(
            core.Id,
            core.Name,
            core.AvatarUrl,
            core.Bio,
            core.YearsOfExperience,
            core.AverageRating,
            core.TotalReviews,
            core.TotalCompletedBookings,
            core.IsVerified,
            inoMembership,
            JsonCodeList.Parse(core.SpecializationsJson),
            services.Select(s => new NursePublicServiceDto(
                s.Id, s.DisplayName, s.Price.ToString(CultureInfo.InvariantCulture), s.PriceUnit, s.SessionCount)).ToList(),
            latest is null ? null : new NursePublicLatestReviewDto(latest.Rating, latest.Body, null, latest.CreatedAt));
    }
}
