using Application.Abstractions.Identity;
using Application.Abstractions.Identity.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Persistence.Identity
{
    public sealed class IdentityUserReader(UserManager<ApplicationUser> userManager, IdentityDbContext db) : IIdentityUserReader
    {
        public async Task<AspNetUserDto?> GetUserAsync(string id, CancellationToken ct)
        {
            if (string.IsNullOrWhiteSpace(id))
            {
                return null;
            }

            var user = await userManager.FindByIdAsync(id);
            if (user is null)
            {
                return null;
            }

            var roles = await userManager.GetRolesAsync(user);

            return new AspNetUserDto
            {
                ID = user.Id,
                UserName = user.UserName,
                NormalizedUserName = user.NormalizedUserName,
                Email = user.Email,
                NormalizedEmail = user.NormalizedEmail,
                EmailConfirmed = user.EmailConfirmed,
                PhoneNumber = user.PhoneNumber,
                PhoneNumberConfirmed = user.PhoneNumberConfirmed,
                TwoFactorEnabled = user.TwoFactorEnabled,
                LockoutEnd = user.LockoutEnd,
                LockoutEnabled = user.LockoutEnabled,
                AccessFailedCount = user.AccessFailedCount,
                FullName = user.FullName,
                IsDeleted = user.IsDeleted,
                Roles = [..roles]
            };
        }

        public async Task<List<AspNetUserDto>> GetAllUserAsync(CancellationToken ct)
        {
            var rows = await (
                from u in db.Users.AsNoTracking()
                join ur in db.UserRoles.AsNoTracking() on u.Id equals ur.UserId into userRoles
                from ur in userRoles.DefaultIfEmpty()
                join r in db.Roles.AsNoTracking() on ur.RoleId equals r.Id into roles
                from r in roles.DefaultIfEmpty()
                select new
                {
                    User = u,
                    RoleName = r != null ? r.Name : null
                }
            ).ToListAsync();

            return [..rows.GroupBy(x => x.User.Id).Select(g =>
            {
                var u = g.First().User;
                var roles = g.Select(x => x.RoleName).Where(rn => rn != null).ToList();

                return new AspNetUserDto
                {
                    ID = u.Id,
                    UserName = u.UserName,
                    NormalizedUserName = u.NormalizedUserName,
                    Email = u.Email,
                    NormalizedEmail = u.NormalizedEmail,
                    EmailConfirmed = u.EmailConfirmed,
                    PhoneNumber = u.PhoneNumber,
                    PhoneNumberConfirmed = u.PhoneNumberConfirmed,
                    TwoFactorEnabled = u.TwoFactorEnabled,
                    LockoutEnd = u.LockoutEnd,
                    LockoutEnabled = u.LockoutEnabled,
                    AccessFailedCount = u.AccessFailedCount,
                    FullName = u.FullName,
                    IsDeleted = u.IsDeleted,
                    Roles = roles
                };
            })];
        }
    }
}