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

namespace MktXService.Infrastructure.Repositories;

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

    public IUnitOfWork UnitOfWork => _context;

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

    public Contact Add(Contact contact)
    {
        return _context.Contacts.Add(contact).Entity;
    }

    public void Update(Contact contact)
    {
        _context.Entry(contact).State = EntityState.Modified;
    }

    public void Delete(Contact contact)
    {
        _context.Contacts.Remove(contact);
    }

    public async Task<Contact?> GetByIdAsync(Guid id)
    {
        return await _context.Contacts
            .Include(c => c.Tags)
            .FirstOrDefaultAsync(c => c.Id == id);
    }

    public async Task<Contact?> GetByTwitterUserIdAsync(Guid accountId, string twitterUserId)
    {
        return await _context.Contacts
            .Include(c => c.Tags)
            .FirstOrDefaultAsync(c => 
                EF.Property<Guid>(c, "_accountId") == accountId &&
                EF.Property<string>(c, "_twitterUserId") == twitterUserId);
    }

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

    public async Task<IEnumerable<Contact>> GetByTagsAsync(Guid accountId, IEnumerable<string> tags, int skip = 0, int take = 20)
    {
        var tagList = tags.Select(t => t.ToLowerInvariant().Trim()).ToList();

        return await _context.Contacts
            .Include(c => c.Tags)
            .Where(c => EF.Property<Guid>(c, "_accountId") == accountId)
            .Where(c => c.Tags.Any(t => tagList.Contains(t.Name)))
            .OrderByDescending(c => EF.Property<DateTime?>(c, "_lastInteractionAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Contact>> SearchAsync(Guid accountId, string searchTerm, int skip = 0, int take = 20)
    {
        var term = searchTerm.ToLowerInvariant();

        return await _context.Contacts
            .Include(c => c.Tags)
            .Where(c => EF.Property<Guid>(c, "_accountId") == accountId)
            .Where(c => EF.Property<string>(c, "_username").ToLower().Contains(term) ||
                        (EF.Property<string?>(c, "_displayName") != null && 
                         EF.Property<string?>(c, "_displayName")!.ToLower().Contains(term)))
            .OrderByDescending(c => EF.Property<DateTime?>(c, "_lastInteractionAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<int> CountByAccountIdAsync(Guid accountId)
    {
        return await _context.Contacts
            .CountAsync(c => EF.Property<Guid>(c, "_accountId") == accountId);
    }
}
