using Microsoft.EntityFrameworkCore;
using Modules.User.Database.Database;
using Modules.User.Database.Database.Entities;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

namespace Modules.User.Database.Repositories;

public class SessionRepository(UserDbContext context)
{
    private readonly UserDbContext _context = context;

    public async Task<List<Session>> GetAll(Guid accountId) =>
        await _context.Sessions.Where(q => q.AccountId == accountId).ToListAsync();

    public async Task<Session?> GetFirstOrDefaultWhere(Expression<Func<Session, bool>> predicate) =>
        await _context.Sessions.SingleOrDefaultAsync(predicate);

    public async Task<bool> SyncSessions(Guid accountId, List<Session> sessions, List<Guid>? forceUpdateSessionIds = null)
    {
        var accountSessions = await GetAll(accountId);
        var deleteSessions = accountSessions.Except(sessions, new SessionComparer()).ToList();
        var addSessions = sessions.Except(accountSessions, new SessionComparer()).ToList();
        var updateSessions = sessions
            .Where(q => accountSessions.Any(x => x.Id == q.Id && x.AccountId == q.AccountId)).ToList();
        if (forceUpdateSessionIds?.Count > 0)
        {
            var sessionsToForceUpdate = forceUpdateSessionIds.Where(x => !deleteSessions.Any(z => z.Id.Equals(x)) &&
                                    x != Guid.Empty && !updateSessions.Any(z => z.Id.Equals(x)));
            updateSessions.AddRange(sessions.Where(q => sessionsToForceUpdate.Contains(q.Id)));
        }
        
        var result = true;

        if (deleteSessions.Count != 0)
        {
            foreach(var session in deleteSessions)
            {
                _context.Sessions.Remove(accountSessions.First(q => q.Id == session.Id));
            }
        }
        if (addSessions.Count != 0 && result)
        {
            _context.Sessions.AddRange(addSessions);
        }

        if (updateSessions.Count != 0 && result)
        {
            foreach (var session in updateSessions)
            {
                var existingSession = accountSessions.First(q => q.Id == session.Id);
                existingSession.ClientInfo = new ClientInfo
                {
                    UserAgent = session.ClientInfo.UserAgent,
                    Country = session.ClientInfo.Country,
                    Region = session.ClientInfo.Region,
                };
                if (existingSession.RefreshToken != session.RefreshToken ||
                    existingSession.ExpiredDate != session.ExpiredDate)
                {
                    existingSession.RefreshToken = session.RefreshToken;
                    existingSession.ExpiredDate = session.ExpiredDate;
                    existingSession.LastUpdate = DateTime.UtcNow;
                }
            }
        }
        await _context.SaveChangesAsync();
        return result;
    }

    private class SessionComparer : IEqualityComparer<Session>
    {
        public bool Equals(Session? x, Session? y)
        {
            return x != null && y != null &&
                x.Id != default && y.Id != default && x.Id == y.Id &&
                x.AccountId == y.AccountId;
        }

        public int GetHashCode([DisallowNull] Session obj)
        {
            return HashCode.Combine(obj.Id.GetHashCode(), obj.AccountId.GetHashCode());
        }
    }

    //public async Task<bool> DeleteAsync(Session entity, bool cleanExpired = true)
    //{
    //    var document = await _collection.FindOneAndDeleteAsync(q => q.RefreshToken == entity.RefreshToken);
    //    if (cleanExpired) await CleanAccountSessions(entity.AccountId);
    //    return document != null;
    //}

    //public async Task CleanAccountSessions(Guid accountId) => await DeleteAccountSessions(accountId);

    //private async Task<bool> DeleteAccountSessions(Guid accountId, bool onlyExpired = true)
    //{
    //    var filter = Builders<Session>.Filter.Eq(q => q.AccountId, accountId);
    //    if (onlyExpired) filter = filter & Builders<Session>.Filter.Lt(q => q.ExpiredDate, DateTime.UtcNow);
    //    var deleteResult = await _collection.DeleteManyAsync(filter);
    //    return deleteResult.IsAcknowledged;
    //}
}