using System.Text.Json;
using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Synth.Models;
using Managing.Infrastructure.Databases.PostgreSql.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Managing.Infrastructure.Databases.PostgreSql;

public class PostgreSqlSynthRepository : ISynthRepository
{
    private readonly ManagingDbContext _context;
    private readonly ILogger<PostgreSqlSynthRepository> _logger;

    public PostgreSqlSynthRepository(ManagingDbContext context, ILogger<PostgreSqlSynthRepository> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<SynthMinersLeaderboard?> GetLeaderboardAsync(string cacheKey)
    {
        try
        {
            var entity = await _context.SynthMinersLeaderboards.AsNoTracking().FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
            if (entity == null)
            {
                _logger.LogDebug($"[PG Synth] No leaderboard cache found for key: {cacheKey}");
                return null;
            }
            var miners = JsonSerializer.Deserialize<List<MinerInfo>>(entity.MinersData);
            return new SynthMinersLeaderboard
            {
                Id = entity.Id.ToString(),
                Asset = entity.Asset,
                TimeIncrement = entity.TimeIncrement,
                SignalDate = entity.SignalDate,
                IsBacktest = entity.IsBacktest,
                Miners = miners ?? new List<MinerInfo>(),
                CreatedAt = entity.CreatedAt
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error retrieving leaderboard cache for key: {cacheKey}");
            return null;
        }
    }

    public async Task SaveLeaderboardAsync(SynthMinersLeaderboard leaderboard)
    {
        try
        {
            leaderboard.CreatedAt = DateTime.UtcNow;
            var cacheKey = leaderboard.GetCacheKey();
            var minersData = JsonSerializer.Serialize(leaderboard.Miners);
            var existing = await _context.SynthMinersLeaderboards.FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
            if (existing != null)
            {
                existing.Asset = leaderboard.Asset;
                existing.TimeIncrement = leaderboard.TimeIncrement;
                existing.SignalDate = leaderboard.SignalDate;
                existing.IsBacktest = leaderboard.IsBacktest;
                existing.MinersData = minersData;
                existing.CreatedAt = leaderboard.CreatedAt;
                _context.SynthMinersLeaderboards.Update(existing);
                _logger.LogDebug($"[PG Synth] Updated leaderboard in DB for key: {cacheKey}");
            }
            else
            {
                var entity = new SynthMinersLeaderboardEntity
                {
                    Id = Guid.NewGuid(),
                    Asset = leaderboard.Asset,
                    TimeIncrement = leaderboard.TimeIncrement,
                    SignalDate = leaderboard.SignalDate,
                    IsBacktest = leaderboard.IsBacktest,
                    MinersData = minersData,
                    CacheKey = cacheKey,
                    CreatedAt = leaderboard.CreatedAt
                };
                await _context.SynthMinersLeaderboards.AddAsync(entity);
                _logger.LogDebug($"[PG Synth] Saved new leaderboard to DB for key: {cacheKey}");
            }
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error saving leaderboard cache for key: {leaderboard.GetCacheKey()}");
        }
    }

    public async Task<List<SynthPrediction>> GetIndividualPredictionsAsync(string asset, int timeIncrement, int timeLength, List<int> minerUids, bool isBacktest, DateTime? signalDate)
    {
        try
        {
            var results = new List<SynthPrediction>();
            foreach (var minerUid in minerUids)
            {
                var cacheKey = $"{asset}_{timeIncrement}_{timeLength}_{minerUid}";
                if (isBacktest && signalDate.HasValue)
                {
                    cacheKey += $"_backtest_{signalDate.Value:yyyy-MM-dd-HH}";
                }
                var entity = await _context.SynthPredictions.AsNoTracking().FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
                if (entity != null)
                {
                    var prediction = JsonSerializer.Deserialize<MinerPrediction>(entity.PredictionData);
                    if (prediction != null)
                    {
                        results.Add(new SynthPrediction
                        {
                            Id = entity.Id.ToString(),
                            Asset = entity.Asset,
                            MinerUid = entity.MinerUid,
                            TimeIncrement = entity.TimeIncrement,
                            TimeLength = entity.TimeLength,
                            SignalDate = entity.SignalDate,
                            IsBacktest = entity.IsBacktest,
                            Prediction = prediction,
                            CreatedAt = entity.CreatedAt
                        });
                    }
                }
            }
            if (results.Any())
            {
                _logger.LogDebug($"[PG Synth] Retrieved {results.Count}/{minerUids.Count} individual predictions for {asset}");
            }
            else
            {
                _logger.LogDebug($"[PG Synth] No individual predictions found for {asset}");
            }
            return results;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error retrieving individual predictions cache for asset: {asset}");
            return new List<SynthPrediction>();
        }
    }

    public async Task SaveIndividualPredictionAsync(SynthPrediction prediction)
    {
        try
        {
            prediction.CreatedAt = DateTime.UtcNow;
            var cacheKey = prediction.GetCacheKey();
            var predictionData = JsonSerializer.Serialize(prediction.Prediction);
            var existing = await _context.SynthPredictions.FirstOrDefaultAsync(x => x.CacheKey == cacheKey).ConfigureAwait(false);
            if (existing != null)
            {
                existing.Asset = prediction.Asset;
                existing.MinerUid = prediction.MinerUid;
                existing.TimeIncrement = prediction.TimeIncrement;
                existing.TimeLength = prediction.TimeLength;
                existing.SignalDate = prediction.SignalDate;
                existing.IsBacktest = prediction.IsBacktest;
                existing.PredictionData = predictionData;
                existing.CreatedAt = prediction.CreatedAt;
                _context.SynthPredictions.Update(existing);
                _logger.LogDebug($"[PG Synth] Updated individual prediction for miner {prediction.MinerUid}");
            }
            else
            {
                var entity = new SynthPredictionEntity
                {
                    Id = Guid.NewGuid(),
                    Asset = prediction.Asset,
                    MinerUid = prediction.MinerUid,
                    TimeIncrement = prediction.TimeIncrement,
                    TimeLength = prediction.TimeLength,
                    SignalDate = prediction.SignalDate,
                    IsBacktest = prediction.IsBacktest,
                    PredictionData = predictionData,
                    CacheKey = cacheKey,
                    CreatedAt = prediction.CreatedAt
                };
                await _context.SynthPredictions.AddAsync(entity).ConfigureAwait(false);
                _logger.LogDebug($"[PG Synth] Saved new individual prediction for miner {prediction.MinerUid}");
            }
            await _context.SaveChangesAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error saving individual prediction cache for miner {prediction.MinerUid}: {ex.Message}");
        }
    }

    public async Task SaveIndividualPredictionsAsync(List<SynthPrediction> predictions)
    {
        if (!predictions.Any())
            return;
        try
        {
            var saveTasks = predictions.Select(SaveIndividualPredictionAsync);
            await Task.WhenAll(saveTasks);
            _logger.LogInformation($"[PG Synth] Successfully saved {predictions.Count} individual predictions to DB");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error saving batch of {predictions.Count} individual predictions");
        }
    }

    public async Task CleanupOldDataAsync(int retentionDays = 30)
    {
        try
        {
            var cutoffDate = DateTime.UtcNow.AddDays(-retentionDays);
            var oldLeaderboards = _context.SynthMinersLeaderboards.Where(x => x.CreatedAt < cutoffDate);
            _context.SynthMinersLeaderboards.RemoveRange(oldLeaderboards);
            var oldPredictions = _context.SynthPredictions.Where(x => x.CreatedAt < cutoffDate);
            _context.SynthPredictions.RemoveRange(oldPredictions);
            await _context.SaveChangesAsync();
            _logger.LogInformation($"[PG Synth] Cleaned up old Synth cache data older than {retentionDays} days");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error during cleanup of old Synth cache data");
        }
    }
} 