using System.Text.Json;
using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Backtests;
using Managing.Domain.Users;
using Microsoft.EntityFrameworkCore;

namespace Managing.Infrastructure.Databases.PostgreSql;

public class PostgreSqlGeneticRepository : IGeneticRepository
{
    private readonly ManagingDbContext _context;

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

    public void InsertGeneticRequestForUser(User user, GeneticRequest geneticRequest)
    {
        geneticRequest.User = user;
        var geneticRequestEntity = PostgreSqlMappers.Map(geneticRequest);

        // Handle User relationship - check if user exists or create new one
        if (user != null)
        {
            var existingUser = _context.Users
                .AsTracking() // Explicitly enable tracking for this operation
                .FirstOrDefault(u => u.Name == user.Name);
            if (existingUser != null)
            {
                geneticRequestEntity.UserId = existingUser.Id;
                geneticRequestEntity.User = null; // Prevent EF from trying to insert duplicate user
            }
            else
            {
                // Let EF handle the new user creation
                geneticRequestEntity.UserId = null;
            }
        }

        _context.GeneticRequests.Add(geneticRequestEntity);
        _context.SaveChanges();
    }

    public IEnumerable<GeneticRequest> GetGeneticRequestsByUser(User user)
    {
        // Use synchronous operations and AsNoTracking to avoid concurrency issues
        var geneticRequestEntities = _context.GeneticRequests
            .AsNoTracking()
            .Include(gr => gr.User)
            .Where(gr => gr.User != null && gr.User.Name == user.Name)
            .OrderByDescending(gr => gr.CreatedAt)
            .ToList();

        return PostgreSqlMappers.Map(geneticRequestEntities);
    }

    public GeneticRequest GetGeneticRequestByIdForUser(User user, string id)
    {
        // Use synchronous operations and AsNoTracking to avoid concurrency issues
        var geneticRequestEntity = _context.GeneticRequests
            .AsNoTracking()
            .Include(gr => gr.User)
            .FirstOrDefault(gr => gr.RequestId == id);

        // Check if genetic request exists and belongs to the user
        if (geneticRequestEntity != null && geneticRequestEntity.User != null &&
            geneticRequestEntity.User.Name == user.Name)
        {
            return PostgreSqlMappers.Map(geneticRequestEntity);
        }

        return null;
    }

    public async Task UpdateGeneticRequestAsync(GeneticRequest geneticRequest)
    {
        var existingEntity = _context.GeneticRequests
            .Include(gr => gr.User)
            .FirstOrDefault(gr => gr.RequestId == geneticRequest.RequestId);

        if (existingEntity != null)
        {
            // Update the existing entity with new values
            existingEntity.CompletedAt = geneticRequest.CompletedAt;
            existingEntity.UpdatedAt = DateTime.UtcNow;
            existingEntity.Status = geneticRequest.Status.ToString();
            existingEntity.BestFitness = geneticRequest.BestFitness;
            existingEntity.BestIndividual = geneticRequest.BestIndividual;
            existingEntity.ErrorMessage = geneticRequest.ErrorMessage;
            existingEntity.ProgressInfo = geneticRequest.ProgressInfo;
            existingEntity.BestChromosome = geneticRequest.BestChromosome;
            existingEntity.BestFitnessSoFar = geneticRequest.BestFitnessSoFar;
            existingEntity.CurrentGeneration = geneticRequest.CurrentGeneration;

            // Update EligibleIndicators JSON
            if (geneticRequest.EligibleIndicators != null && geneticRequest.EligibleIndicators.Any())
            {
                try
                {
                    existingEntity.EligibleIndicatorsJson = JsonSerializer.Serialize(geneticRequest.EligibleIndicators);
                }
                catch
                {
                    existingEntity.EligibleIndicatorsJson = "[]";
                }
            }
            else
            {
                existingEntity.EligibleIndicatorsJson = "[]";
            }

            // Only update the tracked entity, do not attach a new one
            await _context.SaveChangesAsync();
        }
    }

    public void DeleteGeneticRequestByIdForUser(User user, string id)
    {
        var geneticRequestEntity = _context.GeneticRequests
            .Include(gr => gr.User)
            .FirstOrDefault(gr => gr.RequestId == id);

        if (geneticRequestEntity != null && geneticRequestEntity.User != null &&
            geneticRequestEntity.User.Name == user.Name)
        {
            _context.GeneticRequests.Remove(geneticRequestEntity);
            _context.SaveChanges();
        }
    }

    public void DeleteAllGeneticRequestsForUser(User user)
    {
        var geneticRequestEntities = _context.GeneticRequests
            .Include(gr => gr.User)
            .Where(gr => gr.User != null && gr.User.Name == user.Name)
            .ToList();

        if (geneticRequestEntities.Any())
        {
            _context.GeneticRequests.RemoveRange(geneticRequestEntities);
            _context.SaveChanges();
        }
    }

    public async Task<List<GeneticRequest>> GetGeneticRequestsAsync(GeneticRequestStatus status)
    {
        var requests = await _context.GeneticRequests
            .AsNoTracking()
            .Include(gr => gr.User)
            .Where(gr => gr.Status == status.ToString())
            .OrderBy(gr => gr.CreatedAt)
            .ToListAsync();
        return PostgreSqlMappers.Map(requests).ToList();
    }
}