using Managing.Application.Abstractions.Repositories;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Users;
using Microsoft.EntityFrameworkCore;

namespace Managing.Infrastructure.Databases.PostgreSql;

public class PostgreSqlSettingsRepository : ISettingsRepository
{
    private readonly ManagingDbContext _context;

    public PostgreSqlSettingsRepository(ManagingDbContext context)
    {
        _context = context;
    }

    public async Task DeleteMoneyManagementAsync(string name)
    {
        var moneyManagement = await _context.MoneyManagements
            .AsTracking()
            .FirstOrDefaultAsync(m => m.Name == name)
            .ConfigureAwait(false);

        if (moneyManagement != null)
        {
            _context.MoneyManagements.Remove(moneyManagement);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    public async Task DeleteMoneyManagementsAsync()
    {
        var moneyManagements = await _context.MoneyManagements
            .AsTracking()
            .ToListAsync()
            .ConfigureAwait(false);

        _context.MoneyManagements.RemoveRange(moneyManagements);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task<MoneyManagement> GetMoneyManagement(string name)
    {
        var moneyManagement = await _context.MoneyManagements
            .AsNoTracking()
            .Include(m => m.User)
            .FirstOrDefaultAsync(m => m.Name == name)
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(moneyManagement);
    }

    public async Task<IEnumerable<MoneyManagement>> GetMoneyManagementsAsync()
    {
        var moneyManagements = await _context.MoneyManagements
            .AsNoTracking()
            .Include(m => m.User)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(moneyManagements);
    }

    public async Task InsertMoneyManagement(LightMoneyManagement request, User user)
    {
        // Check if money management already exists for the same user
        var existingMoneyManagement = await _context.MoneyManagements
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.Name == request.Name &&
                                      ((user == null && m.UserName == null) ||
                                       (user != null && m.UserName == user.Name)))
            .ConfigureAwait(false);

        if (existingMoneyManagement != null)
        {
            throw new InvalidOperationException(
                $"Money management with name '{request.Name}' already exists for user '{user?.Name}'");
        }

        var entity = PostgreSqlMappers.Map(request);
        entity.UserName = user?.Name;
        
        // Set the UserId if user is provided
        if (user != null)
        {
            var userEntity = await _context.Users
                .AsNoTracking()
                .FirstOrDefaultAsync(u => u.Name == user.Name)
                .ConfigureAwait(false);
            entity.UserId = userEntity?.Id;
        }
        
        await _context.MoneyManagements.AddAsync(entity).ConfigureAwait(false);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task UpdateMoneyManagementAsync(LightMoneyManagement moneyManagement, User user)
    {
        var entity = await _context.MoneyManagements
            .AsTracking()
            .FirstOrDefaultAsync(m => m.Name == moneyManagement.Name &&
                                      (user != null && m.UserName == user.Name))
            .ConfigureAwait(false);

        if (entity != null)
        {
            entity.Timeframe = moneyManagement.Timeframe;
            entity.StopLoss = moneyManagement.StopLoss;
            entity.TakeProfit = moneyManagement.TakeProfit;
            entity.Leverage = moneyManagement.Leverage;
            entity.UserName = user?.Name;
            entity.UpdatedAt = DateTime.UtcNow;

            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    // User-specific implementations
    public async Task<MoneyManagement> GetMoneyManagementByUser(User user, string name)
    {
        var moneyManagement = await _context.MoneyManagements
            .AsNoTracking()
            .Include(m => m.User)
            .FirstOrDefaultAsync(m =>
                m.Name == name &&
                m.UserName != null &&
                m.UserName == user.Name)
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(moneyManagement);
    }

    public async Task<IEnumerable<MoneyManagement>> GetMoneyManagementsByUserAsync(User user)
    {
        var moneyManagements = await _context.MoneyManagements
            .AsNoTracking()
            .Include(m => m.User)
            .Where(m => m.UserName != null && m.UserName == user.Name)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(moneyManagements);
    }

    public async Task DeleteMoneyManagementByUserAsync(User user, string name)
    {
        var moneyManagement = await _context.MoneyManagements
            .AsTracking()
            .FirstOrDefaultAsync(m =>
                m.Name == name &&
                m.UserName != null &&
                m.UserName == user.Name)
            .ConfigureAwait(false);

        if (moneyManagement != null)
        {
            _context.MoneyManagements.Remove(moneyManagement);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    public async Task DeleteMoneyManagementsByUserAsync(User user)
    {
        var moneyManagements = await _context.MoneyManagements
            .AsTracking()
            .Where(m => m.UserName != null && m.UserName == user.Name)
            .ToListAsync()
            .ConfigureAwait(false);

        _context.MoneyManagements.RemoveRange(moneyManagements);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }
}