using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Bots;
using Microsoft.EntityFrameworkCore;
using static Managing.Common.Enums;

namespace Managing.Infrastructure.Databases.PostgreSql;

public class PostgreSqlBotRepository : IBotRepository
{
    private readonly ManagingDbContext _context;

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

    public async Task InsertBotAsync(Bot bot)
    {
        bot.CreateDate = DateTime.UtcNow;
        var entity = PostgreSqlMappers.Map(bot);
        // Set the UserId if user is provided
        if (bot.User != null)
        {
            var userEntity = await _context.Users
                .AsNoTracking()
                .FirstOrDefaultAsync(u => u.Id == bot.User.Id)
                .ConfigureAwait(false);

            if (userEntity == null)
            {
                throw new InvalidOperationException($"User with id '{bot.User.Id}' not found");
            }

            entity.UserId = userEntity.Id;
        }

        await _context.Bots.AddAsync(entity).ConfigureAwait(false);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task<IEnumerable<Bot>> GetBotsAsync()
    {
        var entities = await _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities);
    }

    public async Task UpdateBot(Bot bot)
    {
        var existingEntity = await _context.Bots
            .AsTracking()
            .FirstOrDefaultAsync(b => b.Identifier == bot.Identifier)
            .ConfigureAwait(false);

        if (existingEntity == null)
        {
            throw new InvalidOperationException($"Bot backup with identifier '{bot.Identifier}' not found");
        }

        // Update the existing entity properties directly instead of creating a new one
        existingEntity.Name = bot.Name;
        existingEntity.Ticker = bot.Ticker;
        existingEntity.Status = bot.Status;
        existingEntity.StartupTime = bot.StartupTime;
        existingEntity.TradeWins = bot.TradeWins;
        existingEntity.TradeLosses = bot.TradeLosses;
        existingEntity.Pnl = bot.Pnl;
        existingEntity.Roi = bot.Roi;
        existingEntity.Volume = bot.Volume;
        existingEntity.Fees = bot.Fees;
        existingEntity.UpdatedAt = DateTime.UtcNow;
        existingEntity.LongPositionCount = bot.LongPositionCount;
        existingEntity.ShortPositionCount = bot.ShortPositionCount;

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

    public async Task DeleteBot(Guid identifier)
    {
        var entity = await _context.Bots
            .AsTracking()
            .FirstOrDefaultAsync(b => b.Identifier == identifier)
            .ConfigureAwait(false);

        if (entity == null)
        {
            throw new InvalidOperationException($"Bot backup with identifier '{identifier}' not found");
        }

        _context.Bots.Remove(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task<Bot> GetBotByIdentifierAsync(Guid identifier)
    {
        var entity = await _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .FirstOrDefaultAsync(b => b.Identifier == identifier)
            .ConfigureAwait(false);
        return PostgreSqlMappers.Map(entity);
    }

    public async Task<IEnumerable<Bot>> GetBotsByUserIdAsync(int id)
    {
        var entities = await _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .Where(b => b.UserId == id)
            .ToListAsync()
            .ConfigureAwait(false);
        return PostgreSqlMappers.Map(entities);
    }

    public async Task<IEnumerable<Bot>> GetBotsByStatusAsync(BotStatus status)
    {
        var entities = await _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .Where(b => b.Status == status)
            .ToListAsync()
            .ConfigureAwait(false);
        return PostgreSqlMappers.Map(entities);
    }

    public async Task<Bot> GetBotByNameAsync(string name)
    {
        var entity = await _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .FirstOrDefaultAsync(b => b.Name == name)
            .ConfigureAwait(false);
        return PostgreSqlMappers.Map(entity);
    }

    public async Task<IEnumerable<Bot>> GetBotsByIdsAsync(IEnumerable<Guid> identifiers)
    {
        try
        {
            await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);

            var entities = await _context.Bots
                .AsNoTracking()
                .Include(m => m.User)
                .Where(b => identifiers.Contains(b.Identifier))
                .ToListAsync()
                .ConfigureAwait(false);

            return PostgreSqlMappers.Map(entities);
        }
        finally
        {
            // Always ensure the connection is closed after the operation
            await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
        }
    }

    public async Task<(IEnumerable<Bot> Bots, int TotalCount)> GetBotsPaginatedAsync(
        int pageNumber,
        int pageSize,
        BotStatus? status = null,
        string? name = null,
        string? ticker = null,
        string? agentName = null,
        string sortBy = "CreateDate",
        string sortDirection = "Desc")
    {
        // Build the query with filters
        var query = _context.Bots
            .AsNoTracking()
            .Include(m => m.User)
            .AsQueryable();

        // Apply filters
        if (status.HasValue)
        {
            query = query.Where(b => b.Status == status.Value);
        }

        if (!string.IsNullOrWhiteSpace(name))
        {
            query = query.Where(b => EF.Functions.ILike(b.Name, $"%{name}%"));
        }

        if (!string.IsNullOrWhiteSpace(ticker))
        {
            query = query.Where(b => EF.Functions.ILike(b.Ticker.ToString(), $"%{ticker}%"));
        }

        if (!string.IsNullOrWhiteSpace(agentName))
        {
            query = query.Where(b => b.User != null && EF.Functions.ILike(b.User.AgentName, $"%{agentName}%"));
        }

        // Get total count before applying pagination
        var totalCount = await query.CountAsync().ConfigureAwait(false);

        // Apply sorting
        query = sortBy.ToLower() switch
        {
            "name" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.Name)
                : query.OrderByDescending(b => b.Name),
            "ticker" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.Ticker)
                : query.OrderByDescending(b => b.Ticker),
            "status" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.Status)
                : query.OrderByDescending(b => b.Status),
            "startuptime" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.StartupTime)
                : query.OrderByDescending(b => b.StartupTime),
            "pnl" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.Pnl)
                : query.OrderByDescending(b => b.Pnl),
            "winrate" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.TradeWins / (b.TradeWins + b.TradeLosses))
                : query.OrderByDescending(b => b.TradeWins / (b.TradeWins + b.TradeLosses)),
            "agentname" => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.User.AgentName)
                : query.OrderByDescending(b => b.User.AgentName),
            _ => sortDirection.ToLower() == "asc"
                ? query.OrderBy(b => b.CreateDate)
                : query.OrderByDescending(b => b.CreateDate) // Default to CreateDate
        };

        // Apply pagination
        var skip = (pageNumber - 1) * pageSize;
        var entities = await query
            .Skip(skip)
            .Take(pageSize)
            .ToListAsync()
            .ConfigureAwait(false);

        var bots = PostgreSqlMappers.Map(entities);
        return (bots, totalCount);
    }
}