using Microsoft.EntityFrameworkCore;
using SocialService.Domain.AggregatesModel.RelationshipAggregate;
using SocialService.Domain.SeedWork;

namespace SocialService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for Relationship aggregate.
/// VI: Implementation repository cho Relationship aggregate.
/// </summary>
public class RelationshipRepository : IRelationshipRepository
{
    private readonly SocialServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public RelationshipRepository(SocialServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public Relationship Add(Relationship relationship)
    {
        return _context.Relationships.Add(relationship).Entity;
    }

    public Relationship Update(Relationship relationship)
    {
        return _context.Relationships.Update(relationship).Entity;
    }

    public async Task<Relationship?> GetByIdAsync(Guid id)
    {
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .FirstOrDefaultAsync(r => r.Id == id);
    }

    public async Task<Relationship?> GetByUsersAndTypeAsync(Guid requesterId, Guid addresseeId, RelationshipType type)
    {
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .FirstOrDefaultAsync(r => 
                EF.Property<Guid>(r, "_requesterId") == requesterId &&
                EF.Property<Guid>(r, "_addresseeId") == addresseeId &&
                r.TypeId == type.Id);
    }

    public async Task<IEnumerable<Relationship>> GetFriendsAsync(Guid userId, int skip = 0, int take = 20)
    {
        // EN: Get friendships where user is either requester or addressee
        // VI: Lấy các kết bạn mà user là requester hoặc addressee
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .Where(r => r.TypeId == RelationshipType.Friendship.Id &&
                        r.StatusId == RelationshipStatus.Accepted.Id &&
                        (EF.Property<Guid>(r, "_requesterId") == userId ||
                         EF.Property<Guid>(r, "_addresseeId") == userId))
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Relationship>> GetFollowersAsync(Guid userId, int skip = 0, int take = 20)
    {
        // EN: Get users who follow this user (addressee is userId)
        // VI: Lấy các users theo dõi user này (addressee là userId)
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .Where(r => r.TypeId == RelationshipType.Following.Id &&
                        r.StatusId == RelationshipStatus.Accepted.Id &&
                        EF.Property<Guid>(r, "_addresseeId") == userId)
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Relationship>> GetFollowingAsync(Guid userId, int skip = 0, int take = 20)
    {
        // EN: Get users this user is following (requester is userId)
        // VI: Lấy các users mà user này đang theo dõi (requester là userId)
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .Where(r => r.TypeId == RelationshipType.Following.Id &&
                        r.StatusId == RelationshipStatus.Accepted.Id &&
                        EF.Property<Guid>(r, "_requesterId") == userId)
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Relationship>> GetPendingRequestsReceivedAsync(Guid userId, int skip = 0, int take = 20)
    {
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .Where(r => r.TypeId == RelationshipType.Friendship.Id &&
                        r.StatusId == RelationshipStatus.Pending.Id &&
                        EF.Property<Guid>(r, "_addresseeId") == userId)
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Relationship>> GetPendingRequestsSentAsync(Guid userId, int skip = 0, int take = 20)
    {
        return await _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .Where(r => r.TypeId == RelationshipType.Friendship.Id &&
                        r.StatusId == RelationshipStatus.Pending.Id &&
                        EF.Property<Guid>(r, "_requesterId") == userId)
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<bool> AreFriendsAsync(Guid userId1, Guid userId2)
    {
        return await _context.Relationships
            .AnyAsync(r => r.TypeId == RelationshipType.Friendship.Id &&
                          r.StatusId == RelationshipStatus.Accepted.Id &&
                          ((EF.Property<Guid>(r, "_requesterId") == userId1 &&
                            EF.Property<Guid>(r, "_addresseeId") == userId2) ||
                           (EF.Property<Guid>(r, "_requesterId") == userId2 &&
                            EF.Property<Guid>(r, "_addresseeId") == userId1)));
    }

    public async Task<bool> IsFollowingAsync(Guid followerId, Guid followeeId)
    {
        return await _context.Relationships
            .AnyAsync(r => r.TypeId == RelationshipType.Following.Id &&
                          r.StatusId == RelationshipStatus.Accepted.Id &&
                          EF.Property<Guid>(r, "_requesterId") == followerId &&
                          EF.Property<Guid>(r, "_addresseeId") == followeeId);
    }

    public async Task<int> GetFriendsCountAsync(Guid userId)
    {
        return await _context.Relationships
            .CountAsync(r => r.TypeId == RelationshipType.Friendship.Id &&
                            r.StatusId == RelationshipStatus.Accepted.Id &&
                            (EF.Property<Guid>(r, "_requesterId") == userId ||
                             EF.Property<Guid>(r, "_addresseeId") == userId));
    }

    public async Task<int> GetFollowersCountAsync(Guid userId)
    {
        return await _context.Relationships
            .CountAsync(r => r.TypeId == RelationshipType.Following.Id &&
                            r.StatusId == RelationshipStatus.Accepted.Id &&
                            EF.Property<Guid>(r, "_addresseeId") == userId);
    }

    public async Task<int> GetFollowingCountAsync(Guid userId)
    {
        return await _context.Relationships
            .CountAsync(r => r.TypeId == RelationshipType.Following.Id &&
                            r.StatusId == RelationshipStatus.Accepted.Id &&
                            EF.Property<Guid>(r, "_requesterId") == userId);
    }

    #region Admin Methods

    public async Task<IEnumerable<Relationship>> GetAllAsync(int skip, int take, int? statusId = null, int? typeId = null)
    {
        var query = _context.Relationships
            .Include(r => r.Type)
            .Include(r => r.Status)
            .AsQueryable();

        if (statusId.HasValue)
            query = query.Where(r => r.StatusId == statusId.Value);

        if (typeId.HasValue)
            query = query.Where(r => r.TypeId == typeId.Value);

        return await query
            .OrderByDescending(r => EF.Property<DateTime>(r, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<int> GetTotalCountAsync(int? statusId = null, int? typeId = null)
    {
        var query = _context.Relationships.AsQueryable();

        if (statusId.HasValue)
            query = query.Where(r => r.StatusId == statusId.Value);

        if (typeId.HasValue)
            query = query.Where(r => r.TypeId == typeId.Value);

        return await query.CountAsync();
    }

    public void Delete(Relationship relationship)
    {
        _context.Relationships.Remove(relationship);
    }

    #endregion
}

