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

namespace Baya.Infrastructure.Persistence.Repositories;

internal class UserAccountRepository : BaseAsyncRepository<User>, IUserAccountRepository
{
    public UserAccountRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public async Task<UserAccountSnapshot> GetAccountSnapshotAsync(int userId, CancellationToken cancellationToken)
    {
        // UserRoles carries a RevokedAt-is-null global filter, so revoked grants never surface here.
        // The profile flags/verification status are correlated subqueries (soft-delete filters apply), and
        // the enum→code mapping happens after materialization — ToCode() is not SQL-translatable.
        var row = await TableNoTracking
            .Where(u => u.Id == userId)
            .Select(u => new
            {
                u.Id,
                u.PhoneNumber,
                u.Name,
                u.FamilyName,
                u.Gender,
                u.IsActive,
                Roles = u.UserRoles.Select(ur => ur.Role.Name).ToList(),
                HasCustomerProfile = DbContext.Set<CustomerProfile>().Any(p => p.UserId == u.Id),
                HasNurseProfile = DbContext.Set<NurseProfile>().Any(p => p.UserId == u.Id),
                VerificationStatus = DbContext.Set<NurseVerification>()
                    .Where(v => v.Nurse.UserId == u.Id)
                    .OrderByDescending(v => v.Id)
                    .Select(v => (VerificationStatus?)v.Status)
                    .FirstOrDefault()
            })
            .FirstOrDefaultAsync(cancellationToken);

        if (row is null)
            return null;

        return new UserAccountSnapshot(
            row.Id,
            row.PhoneNumber,
            row.Name,
            row.FamilyName,
            row.Gender,
            row.IsActive,
            row.Roles,
            HasCustomerProfile: row.HasCustomerProfile,
            HasNurseProfile: row.HasNurseProfile,
            NurseVerificationStatus: row.VerificationStatus is { } status
                ? status.ToCode()
                : MeResult.VerificationNotStarted);
    }

    public Task<User> GetTrackedByIdAsync(int userId, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(u => u.Id == userId, cancellationToken);

    public Task<Role> GetRoleByNameAsync(string roleName, CancellationToken cancellationToken)
    {
        return DbContext.Set<Role>()
            .FirstOrDefaultAsync(r => r.Name == roleName, cancellationToken);
    }

    public Task<UserRole> GetUserRoleIncludingRevokedAsync(int userId, int roleId, CancellationToken cancellationToken)
    {
        // Bypasses the revoked-filter on purpose: a revoked grant must be re-activated, not
        // re-inserted (composite PK).
        return DbContext.Set<UserRole>()
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(ur => ur.UserId == userId && ur.RoleId == roleId, cancellationToken);
    }

    public async Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken)
    {
        await DbContext.Set<UserRole>().AddAsync(userRole, cancellationToken);
    }
}