using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Statistics;
using Microsoft.EntityFrameworkCore;

namespace Managing.Infrastructure.Databases.PostgreSql;

public class PostgreSqlStatisticRepository : IStatisticRepository
{
    private readonly ManagingDbContext _context;

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

    #region TopVolumeTicker Methods

    public async Task InsertTopVolumeTicker(TopVolumeTicker topVolumeTicker)
    {
        var entity = PostgreSqlMappers.Map(topVolumeTicker);
        await _context.TopVolumeTickers.AddAsync(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public IList<TopVolumeTicker> GetTopVolumeTickers(DateTime date)
    {
        return GetTopVolumeTickersAsync(date).Result;
    }

    public async Task<IList<TopVolumeTicker>> GetTopVolumeTickersAsync(DateTime date)
    {
        var entities = await _context.TopVolumeTickers
            .AsNoTracking()
            .Where(t => date < t.Date)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities).ToList();
    }

    #endregion

    #region SpotlightOverview Methods

    public async Task SaveSpotligthtOverview(SpotlightOverview overview)
    {
        var entity = PostgreSqlMappers.Map(overview);
        await _context.SpotlightOverviews.AddAsync(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public IList<SpotlightOverview> GetSpotlightOverviews(DateTime date)
    {
        return GetSpotlightOverviewsAsync(date).Result;
    }

    public async Task<IList<SpotlightOverview>> GetSpotlightOverviewsAsync(DateTime date)
    {
        var entities = await _context.SpotlightOverviews
            .AsNoTracking()
            .Where(o => date.ToUniversalTime() < o.DateTime)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities).ToList();
    }

    public void UpdateSpotlightOverview(SpotlightOverview overview)
    {
        UpdateSpotlightOverviewAsync(overview).Wait();
    }

    public async Task UpdateSpotlightOverviewAsync(SpotlightOverview overview)
    {
        var existingEntity = await _context.SpotlightOverviews
            .AsTracking()
            .FirstOrDefaultAsync(o => o.Identifier == overview.Identifier)
            .ConfigureAwait(false);

        if (existingEntity != null)
        {
            var updatedEntity = PostgreSqlMappers.Map(overview);
            updatedEntity.Id = existingEntity.Id; // Keep the same ID
            updatedEntity.CreatedAt = existingEntity.CreatedAt; // Keep original creation time
            updatedEntity.UpdatedAt = DateTime.UtcNow;

            _context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    #endregion

    #region BestTrader Methods

    public List<Trader> GetBestTraders()
    {
        return GetBestTradersAsync().Result;
    }

    public async Task<List<Trader>> GetBestTradersAsync()
    {
        var entities = await _context.Traders
            .AsNoTracking()
            .Where(t => t.IsBestTrader)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities).ToList();
    }

    public void UpdateBestTrader(Trader trader)
    {
        UpdateBestTraderAsync(trader).Wait();
    }

    public async Task UpdateBestTraderAsync(Trader trader)
    {
        var existingEntity = await _context.Traders
            .AsTracking()
            .FirstOrDefaultAsync(t => t.Address == trader.Address && t.IsBestTrader)
            .ConfigureAwait(false);

        if (existingEntity != null)
        {
            var updatedEntity = PostgreSqlMappers.Map(trader, true);
            updatedEntity.Id = existingEntity.Id; // Keep the same ID
            updatedEntity.CreatedAt = existingEntity.CreatedAt; // Keep original creation time
            updatedEntity.UpdatedAt = DateTime.UtcNow;

            _context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    public async Task InsertBestTrader(Trader trader)
    {
        var entity = PostgreSqlMappers.Map(trader, true);
        await _context.Traders.AddAsync(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task RemoveBestTrader(Trader trader)
    {
        var entity = await _context.Traders
            .AsTracking()
            .FirstOrDefaultAsync(t => t.Address == trader.Address && t.IsBestTrader)
            .ConfigureAwait(false);

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

    #endregion

    #region BadTrader Methods

    public List<Trader> GetBadTraders()
    {
        return GetBadTradersAsync().Result;
    }

    public async Task<List<Trader>> GetBadTradersAsync()
    {
        var entities = await _context.Traders
            .AsNoTracking()
            .Where(t => !t.IsBestTrader)
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities).ToList();
    }

    public void UpdateBadTrader(Trader trader)
    {
        UpdateBadTraderAsync(trader).Wait();
    }

    public async Task UpdateBadTraderAsync(Trader trader)
    {
        var existingEntity = await _context.Traders
            .AsTracking()
            .FirstOrDefaultAsync(t => t.Address == trader.Address && !t.IsBestTrader)
            .ConfigureAwait(false);

        if (existingEntity != null)
        {
            var updatedEntity = PostgreSqlMappers.Map(trader, false);
            updatedEntity.Id = existingEntity.Id; // Keep the same ID
            updatedEntity.CreatedAt = existingEntity.CreatedAt; // Keep original creation time
            updatedEntity.UpdatedAt = DateTime.UtcNow;

            _context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    public async Task InsertBadTrader(Trader trader)
    {
        var entity = PostgreSqlMappers.Map(trader, false);
        await _context.Traders.AddAsync(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public async Task RemoveBadTrader(Trader trader)
    {
        var entity = await _context.Traders
            .AsTracking()
            .FirstOrDefaultAsync(t => t.Address == trader.Address && !t.IsBestTrader)
            .ConfigureAwait(false);

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

    #endregion

    #region FundingRate Methods

    public List<FundingRate> GetFundingRates()
    {
        return GetFundingRatesAsync().Result;
    }

    public async Task<List<FundingRate>> GetFundingRatesAsync()
    {
        var entities = await _context.FundingRates
            .AsNoTracking()
            .ToListAsync()
            .ConfigureAwait(false);

        return PostgreSqlMappers.Map(entities).ToList();
    }

    public async Task RemoveFundingRate(FundingRate oldRate)
    {
        var entity = await _context.FundingRates
            .AsTracking()
            .FirstOrDefaultAsync(r => r.Ticker == oldRate.Ticker && r.Exchange == oldRate.Exchange)
            .ConfigureAwait(false);

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

    public async Task InsertFundingRate(FundingRate newRate)
    {
        var entity = PostgreSqlMappers.Map(newRate);
        await _context.FundingRates.AddAsync(entity);
        await _context.SaveChangesAsync().ConfigureAwait(false);
    }

    public void UpdateFundingRate(FundingRate oldRate, FundingRate newRate)
    {
        UpdateFundingRateAsync(oldRate, newRate).Wait();
    }

    public async Task UpdateFundingRateAsync(FundingRate oldRate, FundingRate newRate)
    {
        var existingEntity = await _context.FundingRates
            .AsTracking()
            .FirstOrDefaultAsync(r => r.Ticker == oldRate.Ticker && r.Exchange == oldRate.Exchange)
            .ConfigureAwait(false);

        if (existingEntity != null)
        {
            var updatedEntity = PostgreSqlMappers.Map(newRate);
            updatedEntity.Id = existingEntity.Id; // Keep the same ID
            updatedEntity.CreatedAt = existingEntity.CreatedAt; // Keep original creation time
            updatedEntity.UpdatedAt = DateTime.UtcNow;

            _context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

    #endregion
}