using Microsoft.EntityFrameworkCore;
using MktXService.Domain.AggregatesModel.ConversationAggregate;
using MktXService.Domain.SeedWork;

namespace MktXService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for Conversation aggregate.
/// VI: Triển khai repository cho Conversation aggregate.
/// </summary>
public class ConversationRepository : IConversationRepository
{
    private readonly MktXServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public ConversationRepository(MktXServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public Conversation Add(Conversation conversation)
    {
        return _context.Conversations.Add(conversation).Entity;
    }

    public void Update(Conversation conversation)
    {
        _context.Entry(conversation).State = EntityState.Modified;
    }

    public async Task<Conversation?> GetByIdAsync(Guid id)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .FirstOrDefaultAsync(c => c.Id == id);
    }

    public async Task<Conversation?> GetByIdWithMessagesAsync(Guid id)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .Include(c => c.Messages.OrderByDescending(m => EF.Property<DateTime>(m, "_sentAt")))
            .FirstOrDefaultAsync(c => c.Id == id);
    }

    public async Task<Conversation?> GetOpenByContactIdAsync(Guid contactId)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .FirstOrDefaultAsync(c => 
                EF.Property<Guid>(c, "_contactId") == contactId &&
                c.StatusId == ConversationStatus.Open.Id);
    }

    public async Task<IEnumerable<Conversation>> GetByAccountIdAsync(Guid accountId, int skip = 0, int take = 20)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .Where(c => EF.Property<Guid>(c, "_accountId") == accountId)
            .OrderByDescending(c => EF.Property<DateTime?>(c, "_updatedAt") ?? EF.Property<DateTime>(c, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Conversation>> GetByStatusAsync(Guid accountId, ConversationStatus status, int skip = 0, int take = 20)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .Where(c => EF.Property<Guid>(c, "_accountId") == accountId && c.StatusId == status.Id)
            .OrderByDescending(c => EF.Property<DateTime?>(c, "_updatedAt") ?? EF.Property<DateTime>(c, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Conversation>> GetByAssignedUserAsync(Guid userId, int skip = 0, int take = 20)
    {
        return await _context.Conversations
            .Include(c => c.Status)
            .Where(c => EF.Property<Guid?>(c, "_assignedToUserId") == userId)
            .OrderByDescending(c => EF.Property<DateTime?>(c, "_updatedAt") ?? EF.Property<DateTime>(c, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<int> CountOpenByAccountIdAsync(Guid accountId)
    {
        return await _context.Conversations
            .CountAsync(c => 
                EF.Property<Guid>(c, "_accountId") == accountId && 
                c.StatusId == ConversationStatus.Open.Id);
    }
}
