using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RestSharp;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Data.SqlClient;

namespace HorseRacingPredictor.Football
{
    /// <summary>
    /// Classe centralizzata per la gestione delle API di Football
    /// </summary>
    public class Main
    {
        private readonly Manager.Database _database;
        private readonly API.Prediction _predictionManager;
        private readonly API.Fixture _fixtureAPI;
        private readonly API.Odds _oddsAPI;

        // Repository utilizzati per la gestione dei dati
        private readonly Football.Database.League _leagueRepository;
        private readonly Football.Database.Fixture _fixtureRepository;
        private readonly Football.Database.Team _teamRepository;
        private readonly Football.Database.Goals _goalsRepository;
        private readonly Football.Database.Score _scoreRepository;
        private readonly Football.Database.Odds _oddsRepository;
        private readonly Football.Database.Prediction _predictionRepository;

        // Nuovi repository aggiunti
        private readonly Football.Database.BetType _betTypeRepository;
        private readonly Football.Database.Bookmaker _bookmakerRepository;
        private readonly Football.Database.FixtureLeague _fixtureLeagueRepository;
        private readonly Football.Database.Comparison _comparisonRepository;
        private readonly Football.Database.H2H _h2hRepository;
        private readonly Football.Database.LeagueStats _leagueStatsRepository;
        private readonly Football.Database.TeamStats _teamStatsRepository;

        // Aggiungi il repository per le risposte API
        private readonly Football.Database.APIResponse _apiResponseRepository;

        public Main()
        {
            _database = new Manager.Database();
            _predictionManager = new API.Prediction();
            _fixtureAPI = new API.Fixture();
            _oddsAPI = new API.Odds();

            // Inizializzazione dei repository qui invece che nella classe Database
            _leagueRepository = new Football.Database.League();
            _fixtureRepository = new Football.Database.Fixture();
            _teamRepository = new Football.Database.Team();
            _goalsRepository = new Football.Database.Goals();
            _scoreRepository = new Football.Database.Score();
            _oddsRepository = new Football.Database.Odds();
            _predictionRepository = new Football.Database.Prediction();

            // Inizializzazione dei nuovi repository
            _betTypeRepository = new Football.Database.BetType();
            _bookmakerRepository = new Football.Database.Bookmaker();
            _fixtureLeagueRepository = new Football.Database.FixtureLeague();
            _comparisonRepository = new Football.Database.Comparison();
            _h2hRepository = new Football.Database.H2H();
            _leagueStatsRepository = new Football.Database.LeagueStats();
            _teamStatsRepository = new Football.Database.TeamStats();

            // Inizializzazione del repository per le risposte API
            _apiResponseRepository = new Football.Database.APIResponse();
        }

        /// <summary>
        /// Metodo centralizzato per ottenere tutte le partite e le relative quote per una data specifica
        /// Utilizzo della tabella API_Response come intermediario
        /// </summary>
        /// <param name="date">Data per cui recuperare le partite</param>
        /// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
        /// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
        /// <returns>DataTable contenente tutte le partite con le relative quote (quando disponibili)</returns>
        public DataTable GetFixturesAndOdds(DateTime date, IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
        {
            try
            {
                // Prima scarica i dati
                DownloadFixturesAndOdds(date, progressCallback, statusCallback).Wait();
                
                // Poi importa i dati
                return ImportFromApiResponses(progressCallback, statusCallback);
            }
            catch (Exception ex)
            {
                _database.LogError("recupero centralizzato di partite e quote", ex);
                statusCallback?.Report($"Errore: {ex.Message}");
                return CreateEmptyResultTable();
            }
        }

        /// <summary>
        /// Recupera solo l'elenco delle partite per la data specificata e le restituisce come DataTable semplice
        /// </summary>
        public DataTable GetTodayFixtures(DateTime date, IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
        {
            try
            {
                statusCallback?.Report("Scaricamento elenco partite...");
                progressCallback?.Report(10);

                var fixturesResponse = GetFixtures(date);
                progressCallback?.Report(40);

                statusCallback?.Report("Elaborazione partite...");
                var table = CreateFixturesDataTable(fixturesResponse);
                progressCallback?.Report(50);

                statusCallback?.Report("Scaricamento quote...");
                var oddsResponses = GetOdds(date);
                progressCallback?.Report(80);

                statusCallback?.Report("Integrazione quote...");
                ParseOddsIntoTable(table, oddsResponses);
                progressCallback?.Report(100);

                statusCallback?.Report($"Trovate {table.Rows.Count} partite con quote");
                return table;
            }
            catch (Exception ex)
            {
                _database.LogError("recupero partite del giorno", ex);
                statusCallback?.Report($"Errore: {ex.Message}");
                return CreateEmptyResultTable();
            }
        }

        /// <summary>
        /// Scarica i dati dalle API e li salva nella tabella di frontiera API_Response
        /// </summary>
        /// <param name="date">Data per cui recuperare le partite</param>
        /// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
        /// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
        public async Task DownloadFixturesAndOdds(DateTime date, IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
        {
            try
            {
                // Step 1: Informare l'utente che stiamo ottenendo le partite
                statusCallback?.Report("Scaricamento delle partite in corso...");
                progressCallback?.Report(0);

                // Step 2: Recuperare tutte le partite per la data selezionata
                var fixturesResponse = GetFixtures(date);
                progressCallback?.Report(20);

                // Step 3: Informare l'utente che stiamo recuperando le quote
                statusCallback?.Report("Scaricamento delle quote in corso...");
                progressCallback?.Report(40);

                // Step 4: Recuperare le quote per la data selezionata
                var oddsResponses = GetOdds(date);
                progressCallback?.Report(60);

                // Step 5: Informare l'utente che stiamo recuperando le previsioni
                statusCallback?.Report("Scaricamento delle previsioni in corso...");
                progressCallback?.Report(70);

                // Step 6: Recuperare le previsioni per le partite
                await GetPredictionsForFixtures(fixturesResponse);
                progressCallback?.Report(90);

                // Step 7: Operazione completata
                statusCallback?.Report("Scaricamento completato. Dati salvati nella tabella di frontiera.");
                progressCallback?.Report(100);
            }
            catch (Exception ex)
            {
                _database.LogError("scaricamento centralizzato di partite e quote", ex);
                statusCallback?.Report($"Errore: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// Recupera le previsioni per le partite specificate
        /// </summary>
        private async Task GetPredictionsForFixtures(RestResponse fixturesResponse)
        {
            try
            {
                // Verifica che la risposta sia valida
                if (fixturesResponse == null || !fixturesResponse.IsSuccessful || string.IsNullOrEmpty(fixturesResponse.Content))
                {
                    return;
                }

                var json = JsonDocument.Parse(fixturesResponse.Content).RootElement;

                // Verifica che la risposta contenga dati validi
                if (!json.TryGetProperty("response", out var responseElement))
                {
                    return;
                }

                // Per ogni partita, recupera la previsione
                foreach (var item in responseElement.EnumerateArray())
                {
                    try
                    {
                        int fixtureId = item.GetProperty("fixture").GetProperty("id").GetInt32();

                        // Utilizza il prediction manager per ottenere la previsione
                        _predictionManager.GetPredictionByFixture(fixtureId);

                        // Breve pausa per non sovraccaricare l'API
                        await Task.Delay(100);
                    }
                    catch (Exception ex)
                    {
                        _database.LogError($"recupero previsione", ex);
                        // Continua con la prossima partita
                    }
                }
            }
            catch (Exception ex)
            {
                _database.LogError("recupero previsioni per partite", ex);
            }
        }

        /// <summary>
        /// Importa i dati dalla tabella di frontiera API_Response alle tabelle finali
        /// </summary>
        /// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
        /// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
        /// <returns>DataTable contenente tutte le partite con le relative quote (quando disponibili)</returns>
        public DataTable ImportFromApiResponses(IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
        {
            try
            {
                // Step 1: Informare l'utente che stiamo elaborando le risposte API
                statusCallback?.Report("Elaborazione delle risposte API in corso...");
                progressCallback?.Report(0);

                // Step 2: Elaborare le risposte API non elaborate dal database
                ProcessUnprocessedApiResponses();
                progressCallback?.Report(50);

                // Step 3: Creare un DataTable vuoto per i fixture
                var fixturesTable = CreateEmptyFixturesDataTable();

                // Step 4: Recuperare i dati elaborati dal database per visualizzarli
                statusCallback?.Report("Preparazione dei dati per la visualizzazione...");
                var combinedTable = GetProcessedFixturesFromDatabase();
                progressCallback?.Report(90);

                // Step 5: Operazione completata
                statusCallback?.Report("Importazione completata.");
                progressCallback?.Report(100);

                return combinedTable;
            }
            catch (Exception ex)
            {
                _database.LogError("importazione centralizzata di partite e quote", ex);
                statusCallback?.Report($"Errore: {ex.Message}");
                return CreateEmptyResultTable();
            }
        }

        /// <summary>
        /// Recupera le partite per la data specificata utilizzando API.Fixture
        /// </summary>
        private RestResponse GetFixtures(DateTime date)
        {
            return _fixtureAPI.GetFixturesByDate(date);
        }

        /// <summary>
        /// Recupera le quote per la data specificata (potenzialmente più pagine) utilizzando API.Odds
        /// </summary>
        private List<RestResponse> GetOdds(DateTime date)
        {
            var responses = new List<RestResponse>();

            int currentPage = 1;
            int maxPages = 3; // Limita a 3 pagine per evitare troppe chiamate API
            bool hasMorePages = true;

            while (hasMorePages && currentPage <= maxPages)
            {
                var response = _oddsAPI.GetOddsByDate(date, 8, currentPage); // ID 8 = Bet365
                responses.Add(response);

                // Controlla se ci sono altre pagine
                var json = JsonDocument.Parse(response.Content).RootElement;
                if (json.TryGetProperty("paging", out var pagingElement) &&
                    pagingElement.TryGetProperty("total", out var totalElement))
                {
                    int totalPages = totalElement.GetInt32();
                    hasMorePages = currentPage < totalPages;
                }
                else
                {
                    hasMorePages = false;
                }

                currentPage++;
            }

            return responses;
        }

        /// <summary>
        /// Elabora i dati JSON e li inserisce nel database utilizzando transazioni e disabilitazione temporanea dei vincoli
        /// </summary>
        /// <param name="jsonResponse">Risposta JSON da elaborare</param>
        public void ProcessAndInsertData(string jsonResponse)
        {
            try
            {
                var jsonObject = JsonNode.Parse(jsonResponse);

                _database.ExecuteTransactionalQuery("l'elaborazione dei dati calcistici", (connection, transaction) =>
                {
                    try
                    {
                        // Disabilita temporaneamente i vincoli di chiave esterna
                        ((Manager.Database)_database).DisableAllConstraints(connection, transaction);

                        // FASE 1: Inserisci le leghe
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            var league = responseItem["league"];
                            if (league != null && league["id"] != null)
                            {
                                _leagueRepository.Upsert(connection, transaction, league);
                            }
                        }

                        // FASE 2: Inserisci i bookmakers e i tipi di scommessa
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            if (responseItem["bookmakers"] != null)
                            {
                                foreach (var bookmaker in responseItem["bookmakers"].AsArray())
                                {
                                    if (bookmaker["id"] != null)
                                    {
                                        _bookmakerRepository.Upsert(connection, bookmaker);

                                        // Tipi di scommessa
                                        if (bookmaker["bets"] != null)
                                        {
                                            foreach (var bet in bookmaker["bets"].AsArray())
                                            {
                                                if (bet["id"] != null && bet["name"] != null)
                                                {
                                                    _betTypeRepository.Upsert(connection, bet);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // FASE 3: Inserisci le squadre (senza relazione con fixture)
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            var teams = responseItem["teams"];
                            if (teams != null && teams["home"] != null && teams["away"] != null &&
                                teams["home"]["id"] != null && teams["away"]["id"] != null)
                            {
                                // Usa 0 come placeholder per fixtureId - in questa fase inseriamo solo le squadre
                                _teamRepository.UpsertTeams(connection, transaction, teams, 0);
                            }
                        }

                        // FASE 4: Inserisci i fixture e venue
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            var fixture = responseItem["fixture"];
                            if (fixture != null && fixture["id"] != null)
                            {
                                _fixtureRepository.Upsert(connection, transaction, fixture);
                            }
                        }

                        // FASE 5: Inserisci relazioni tra entità e dati dipendenti
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            int? fixtureId = null;
                            try
                            {
                                var fixture = responseItem["fixture"];
                                if (fixture != null && fixture["id"] != null)
                                {
                                    fixtureId = fixture["id"].GetValue<int>();

                                    // Relazioni fixture-team
                                    var teams = responseItem["teams"];
                                    if (teams != null && teams["home"] != null && teams["away"] != null &&
                                        teams["home"]["id"] != null && teams["away"]["id"] != null)
                                    {
                                        _teamRepository.UpsertTeams(connection, transaction, teams, fixtureId.Value);
                                    }

                                    // Relazioni fixture-league
                                    var league = responseItem["league"];
                                    if (league != null && league["id"] != null)
                                    {
                                        int leagueId = league["id"].GetValue<int>();
                                        _fixtureLeagueRepository.Upsert(connection, transaction, fixtureId.Value, leagueId, league);
                                    }

                                    // Goals
                                    var goals = responseItem["goals"];
                                    if (goals != null)
                                    {
                                        _goalsRepository.Upsert(connection, goals, fixtureId.Value);
                                    }

                                    // Score
                                    var score = responseItem["score"];
                                    if (score != null)
                                    {
                                        _scoreRepository.Upsert(connection, score, fixtureId.Value);
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                _database.LogError($"inserimento dati dipendenti per fixture {fixtureId}", ex);
                                // Continuiamo con il prossimo elemento, non interrompiamo l'intera transazione
                            }
                        }

                        // FASE 6: Inserisci dati che richiedono fixture e teams: quote e previsioni
                        foreach (var responseItem in jsonObject["response"].AsArray())
                        {
                            int? fixtureId = null;
                            try
                            {
                                var fixture = responseItem["fixture"];
                                if (fixture != null && fixture["id"] != null)
                                {
                                    fixtureId = fixture["id"].GetValue<int>();

                                    // Quote
                                    if (responseItem["bookmakers"] != null)
                                    {
                                        _oddsRepository.Upsert(connection, responseItem["bookmakers"], fixtureId.Value);
                                    }

                                    // Previsioni
                                    var predictions = responseItem["predictions"];
                                    if (predictions != null)
                                    {
                                        _predictionRepository.Upsert(connection, predictions, fixtureId.Value);

                                        // Recupera ID previsione
                                        int? predictionId = null;
                                        using (var cmd = new SqlCommand("SELECT prediction_id FROM Prediction WHERE fixture_id = @fixture_id", connection, transaction))
                                        {
                                            cmd.Parameters.AddWithValue("@fixture_id", fixtureId.Value);
                                            var result = cmd.ExecuteScalar();
                                            if (result != null && result != DBNull.Value)
                                            {
                                                predictionId = Convert.ToInt32(result);
                                            }
                                        }

                                        if (predictionId.HasValue)
                                        {
                                            // Confronto
                                            if (predictions["comparison"] != null)
                                            {
                                                _comparisonRepository.Upsert(connection, predictionId.Value, predictions["comparison"]);
                                            }

                                            // Head-to-head
                                            if (predictions["h2h"] != null && predictions["h2h"].AsArray().Any())
                                            {
                                                _h2hRepository.DeleteForPrediction(connection, predictionId.Value);

                                                foreach (var h2hFixture in predictions["h2h"].AsArray())
                                                {
                                                    if (h2hFixture["fixture"] != null && h2hFixture["fixture"]["id"] != null)
                                                    {
                                                        _h2hRepository.Insert(connection, predictionId.Value, h2hFixture["fixture"]["id"].GetValue<int>());
                                                    }
                                                }
                                            }

                                            // Statistiche
                                            var teams = responseItem["teams"];
                                            if (predictions["teams"] != null && teams != null)
                                            {
                                                // Home team stats
                                                if (teams["home"] != null && teams["home"]["id"] != null && predictions["teams"]["home"] != null)
                                                {
                                                    int homeTeamId = teams["home"]["id"].GetValue<int>();

                                                    if (predictions["teams"]["home"]["team"] != null)
                                                    {
                                                        _teamStatsRepository.Insert(connection, homeTeamId, predictionId.Value, true,
                                                                             predictions["teams"]["home"]["team"]);
                                                    }

                                                    if (predictions["teams"]["home"]["league"] != null)
                                                    {
                                                        _leagueStatsRepository.Insert(connection, homeTeamId, predictionId.Value, true,
                                                                                 predictions["teams"]["home"]["league"]);
                                                    }
                                                }

                                                // Away team stats
                                                if (teams["away"] != null && teams["away"]["id"] != null && predictions["teams"]["away"] != null)
                                                {
                                                    int awayTeamId = teams["away"]["id"].GetValue<int>();

                                                    if (predictions["teams"]["away"]["team"] != null)
                                                    {
                                                        _teamStatsRepository.Insert(connection, awayTeamId, predictionId.Value, false,
                                                                             predictions["teams"]["away"]["team"]);
                                                    }

                                                    if (predictions["teams"]["away"]["league"] != null)
                                                    {
                                                        _leagueStatsRepository.Insert(connection, awayTeamId, predictionId.Value, false,
                                                                                 predictions["teams"]["away"]["league"]);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                _database.LogError($"inserimento previsioni e quote per fixture {fixtureId}", ex);
                                // Continuiamo con il prossimo elemento
                            }
                        }

                        // Riattiva i vincoli
                        ((Manager.Database)_database).EnableAllConstraints(connection, transaction);
                    }
                    catch (Exception ex)
                    {
                        _database.LogError("processamento dati in transazione", ex);
                        throw; // Rilancia l'eccezione per il rollback della transazione
                    }
                });
            }
            catch (Exception ex)
            {
                _database.LogError("l'elaborazione dei dati calcistici", ex);
            }
        }

        /// <summary>
        /// Aggiorna il database con i dati di partite e quote utilizzando le classi del namespace Football.Database
        /// </summary>
        private void UpdateDatabase(RestResponse fixturesResponse, List<RestResponse> oddsResponses)
        {
            try
            {
                // In questo metodo non elaboriamo più direttamente le risposte
                // Le risposte sono già state salvate nel database dalla classe API
                // e verranno elaborate dal metodo ProcessUnprocessedApiResponses
                
                // Processa le risposte non elaborate
                ProcessUnprocessedApiResponses();
            }
            catch (Exception ex)
            {
                _database.LogError("aggiornamento database", ex);
                throw; // Propaga l'eccezione per gestirla al livello superiore
            }
        }

        /// <summary>
        /// Crea un DataTable vuoto per i fixture
        /// </summary>
        private DataTable CreateEmptyFixturesDataTable()
        {
            var dataTable = new DataTable();

            // Aggiungi colonne
            dataTable.Columns.Add("ID", typeof(int));
            dataTable.Columns.Add("Paese", typeof(string));
            dataTable.Columns.Add("Campionato", typeof(string));
            dataTable.Columns.Add("Data / Ora", typeof(DateTime));
            dataTable.Columns.Add("Stato", typeof(string));
            dataTable.Columns.Add("Casa", typeof(string));
            dataTable.Columns.Add("Trasferta", typeof(string));
            dataTable.Columns.Add("Goals Casa", typeof(int));
            dataTable.Columns.Add("Goals Trasferta", typeof(int));
            dataTable.Columns.Add("Quota Casa", typeof(string));
            dataTable.Columns.Add("Quota Pareggio", typeof(string));
            dataTable.Columns.Add("Quota Trasferta", typeof(string));
            dataTable.Columns.Add("Over 2.5", typeof(string));
            dataTable.Columns.Add("Under 2.5", typeof(string));
            dataTable.Columns.Add("BTTS Sì", typeof(string));
            dataTable.Columns.Add("BTTS No", typeof(string));
            dataTable.Columns.Add("Doppia Casa/X", typeof(string));
            dataTable.Columns.Add("Doppia Casa/Trasf", typeof(string));
            dataTable.Columns.Add("Doppia X/Trasf", typeof(string));
            dataTable.Columns.Add("Previsione", typeof(string));

            return dataTable;
        }

        /// <summary>
        /// Crea un DataTable con le partite dalla risposta API
        /// </summary>
        private DataTable CreateFixturesDataTable(RestResponse response)
        {
            var dataTable = CreateEmptyFixturesDataTable();

            // Verifica che la risposta sia valida
            if (response == null || !response.IsSuccessful || string.IsNullOrEmpty(response.Content))
            {
                return dataTable;
            }

            try
            {
                var json = JsonDocument.Parse(response.Content).RootElement;

                // Verifica che la risposta contenga dati validi
                if (!json.TryGetProperty("response", out var responseElement))
                {
                    return dataTable;
                }

                // Aggiungi righe
                foreach (var item in responseElement.EnumerateArray())
                {
                    try
                    {
                        // Verifica che le proprietà essenziali esistano
                        if (!item.TryGetProperty("fixture", out var fixtureEl) ||
                            !item.TryGetProperty("league", out var leagueEl) ||
                            !item.TryGetProperty("teams", out var teamsEl))
                        {
                            continue;
                        }

                        var row = dataTable.NewRow();

                        row["ID"] = fixtureEl.TryGetProperty("id", out var idEl) ? idEl.GetInt32() : 0;
                        row["Paese"] = leagueEl.TryGetProperty("country", out var countryEl) ? countryEl.GetString() ?? "" : "";
                        row["Campionato"] = leagueEl.TryGetProperty("name", out var leagueNameEl) ? leagueNameEl.GetString() ?? "" : "";

                        if (fixtureEl.TryGetProperty("date", out var dateEl) && dateEl.ValueKind == JsonValueKind.String)
                        {
                            DateTime.TryParse(dateEl.GetString(), out var parsedDate);
                            row["Data / Ora"] = parsedDate;
                        }
                        else
                        {
                            row["Data / Ora"] = DBNull.Value;
                        }

                        row["Stato"] = fixtureEl.TryGetProperty("status", out var statusEl) &&
                                       statusEl.TryGetProperty("long", out var statusLong)
                                       ? statusLong.GetString() ?? "" : "";

                        row["Casa"] = teamsEl.TryGetProperty("home", out var homeEl) &&
                                      homeEl.TryGetProperty("name", out var homeNameEl)
                                      ? homeNameEl.GetString() ?? "" : "";
                        row["Trasferta"] = teamsEl.TryGetProperty("away", out var awayEl) &&
                                           awayEl.TryGetProperty("name", out var awayNameEl)
                                           ? awayNameEl.GetString() ?? "" : "";

                        // Goals (possono essere null per partite non iniziate)
                        if (item.TryGetProperty("goals", out var goalsElement))
                        {
                            row["Goals Casa"] = goalsElement.TryGetProperty("home", out var ghEl) && ghEl.ValueKind == JsonValueKind.Number
                                ? ghEl.GetInt32() : 0;
                            row["Goals Trasferta"] = goalsElement.TryGetProperty("away", out var gaEl) && gaEl.ValueKind == JsonValueKind.Number
                                ? gaEl.GetInt32() : 0;
                        }
                        else
                        {
                            row["Goals Casa"] = 0;
                            row["Goals Trasferta"] = 0;
                        }

                        row["Quota Casa"] = DBNull.Value;
                        row["Quota Pareggio"] = DBNull.Value;
                        row["Quota Trasferta"] = DBNull.Value;
                        row["Over 2.5"] = DBNull.Value;
                        row["Under 2.5"] = DBNull.Value;
                        row["BTTS Sì"] = DBNull.Value;
                        row["BTTS No"] = DBNull.Value;
                        row["Doppia Casa/X"] = DBNull.Value;
                        row["Doppia Casa/Trasf"] = DBNull.Value;
                        row["Doppia X/Trasf"] = DBNull.Value;
                        row["Previsione"] = DBNull.Value;

                        dataTable.Rows.Add(row);
                    }
                    catch (Exception ex)
                    {
                        _database.LogError($"elaborazione riga partita", ex);
                    }
                }
            }
            catch (Exception ex)
            {
                _database.LogError("creazione tabella partite", ex);
            }

            return dataTable;
        }

        /// <summary>
        /// Combina i dati delle partite con le quote
        /// </summary>
        private DataTable CombineFixturesAndOdds(DataTable fixturesTable, List<RestResponse> oddsResponses)
        {
            // Crea una copia del DataTable delle partite
            var combinedTable = fixturesTable.Copy();

            // Se non ci sono risposte di quote o la tabella delle partite è vuota, ritorna la tabella originale
            if (oddsResponses == null || oddsResponses.Count == 0 || combinedTable.Rows.Count == 0)
            {
                return combinedTable;
            }

            try
            {
                // Elabora ogni risposta delle quote
                foreach (var response in oddsResponses)
                {
                    if (!response.IsSuccessful || string.IsNullOrEmpty(response.Content))
                    {
                        continue;
                    }

                    var json = JsonDocument.Parse(response.Content).RootElement;

                    if (!json.TryGetProperty("response", out var responseElement))
                    {
                        continue;
                    }

                    foreach (var item in responseElement.EnumerateArray())
                    {
                        try
                        {
                            int fixtureId = item.GetProperty("fixture").GetProperty("id").GetInt32();

                            // Trova la riga corrispondente nella tabella delle partite
                            DataRow[] matchingRows = combinedTable.Select($"ID = {fixtureId}");
                            if (matchingRows.Length == 0) continue;

                            DataRow row = matchingRows[0];

                            // Cerca le quote del bookmaker (assumiamo Bet365 con ID 8)
                            if (item.TryGetProperty("bookmakers", out var bookmakersElement))
                            {
                                foreach (var bookmaker in bookmakersElement.EnumerateArray())
                                {
                                    if (bookmaker.GetProperty("id").GetInt32() == 8) // Bet365
                                    {
                                        if (bookmaker.TryGetProperty("bets", out var betsElement))
                                        {
                                            foreach (var bet in betsElement.EnumerateArray())
                                            {
                                                if (bet.GetProperty("name").GetString() == "Match Winner")
                                                {
                                                    // Elabora le quote 1X2
                                                    foreach (var value in bet.GetProperty("values").EnumerateArray())
                                                    {
                                                        string betType = value.GetProperty("value").GetString();
                                                        string odd = value.GetProperty("odd").GetString();

                                                        switch (betType)
                                                        {
                                                            case "Home":
                                                                row["Quota Casa"] = odd;
                                                                break;
                                                            case "Draw":
                                                                row["Quota Pareggio"] = odd;
                                                                break;
                                                            case "Away":
                                                                row["Quota Trasferta"] = odd;
                                                                break;
                                                        }
                                                    }

                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                }
                            }

                            // Recupera la previsione per questa partita
                            try
                            {
                                var predictionResponse = _predictionManager.GetPredictionByFixture(fixtureId);
                                if (predictionResponse != null && predictionResponse.IsSuccessful)
                                {
                                    var predictionJson = JsonDocument.Parse(predictionResponse.Content).RootElement;
                                    if (predictionJson.TryGetProperty("response", out var predResponse) &&
                                        predResponse.EnumerateArray().Any())
                                    {
                                        var prediction = predResponse.EnumerateArray().First();
                                        if (prediction.TryGetProperty("predictions", out var predsElement) &&
                                            predsElement.TryGetProperty("winner", out var winnerElement) &&
                                            winnerElement.TryGetProperty("name", out var winnerNameElement))
                                        {
                                            row["Previsione"] = winnerNameElement.GetString();
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                _database.LogError($"recupero previsione per partita ID: {fixtureId}", ex);
                                // Continua con la prossima partita
                            }
                        }
                        catch (Exception ex)
                        {
                            _database.LogError("elaborazione quote partita", ex);
                            // Continua con la prossima partita
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _database.LogError("combinazione partite e quote", ex);
                // In caso di errore, ritorna la tabella originale
            }

            // Ordina le righe per data
            try
            {
                combinedTable.DefaultView.Sort = "Data / Ora ASC";
                return combinedTable.DefaultView.ToTable();
            }
            catch
            {
                return combinedTable; // In caso di errore nell'ordinamento, ritorna la tabella non ordinata
            }
        }

        /// <summary>
        /// Crea un DataTable vuoto in caso di errore
        /// </summary>
        private DataTable CreateEmptyResultTable()
        {
            var table = new DataTable();
            table.Columns.Add("Errore", typeof(string));
            var row = table.NewRow();
            row["Errore"] = "Si è verificato un errore durante il recupero dei dati.";
            table.Rows.Add(row);
            return table;
        }

        /// <summary>
        /// Analizza le risposte delle quote dall'API e le inserisce direttamente nella DataTable delle partite.
        /// Utilizza Bet365 (ID 8) come bookmaker principale; se non presente, usa il primo bookmaker disponibile.
        /// </summary>
        private void ParseOddsIntoTable(DataTable fixturesTable, List<RestResponse> oddsResponses)
        {
            if (oddsResponses == null || oddsResponses.Count == 0 || fixturesTable == null || fixturesTable.Rows.Count == 0)
                return;

            try
            {
                foreach (var response in oddsResponses)
                {
                    if (response == null || !response.IsSuccessful || string.IsNullOrEmpty(response.Content))
                        continue;

                    var json = JsonDocument.Parse(response.Content).RootElement;
                    if (!json.TryGetProperty("response", out var responseElement))
                        continue;

                    foreach (var item in responseElement.EnumerateArray())
                    {
                        try
                        {
                            if (!item.TryGetProperty("fixture", out var fixtureEl))
                                continue;

                            int fixtureId = fixtureEl.GetProperty("id").GetInt32();

                            DataRow[] matchingRows = fixturesTable.Select($"ID = {fixtureId}");
                            if (matchingRows.Length == 0)
                                continue;

                            DataRow row = matchingRows[0];

                            if (!item.TryGetProperty("bookmakers", out var bookmakersEl))
                                continue;

                            // Cerca Bet365 (ID 8), altrimenti usa il primo bookmaker
                            JsonElement selectedBookmaker = default;
                            bool found = false;

                            foreach (var bm in bookmakersEl.EnumerateArray())
                            {
                                if (bm.TryGetProperty("id", out var bmId) && bmId.GetInt32() == 8)
                                {
                                    selectedBookmaker = bm;
                                    found = true;
                                    break;
                                }
                            }

                            if (!found)
                            {
                                var enumerator = bookmakersEl.EnumerateArray();
                                if (enumerator.MoveNext())
                                {
                                    selectedBookmaker = enumerator.Current;
                                    found = true;
                                }
                            }

                            if (!found || !selectedBookmaker.TryGetProperty("bets", out var betsEl))
                                continue;

                            foreach (var bet in betsEl.EnumerateArray())
                            {
                                string betName = bet.TryGetProperty("name", out var nameEl) ? nameEl.GetString() : "";
                                if (string.IsNullOrEmpty(betName) || !bet.TryGetProperty("values", out var valuesEl))
                                    continue;

                                switch (betName)
                                {
                                    case "Match Winner":
                                        foreach (var v in valuesEl.EnumerateArray())
                                        {
                                            string val = GetOddValueString(v, "value");
                                            string odd = GetOddValueString(v, "odd");
                                            if (val == "Home") row["Quota Casa"] = odd;
                                            else if (val == "Draw") row["Quota Pareggio"] = odd;
                                            else if (val == "Away") row["Quota Trasferta"] = odd;
                                        }
                                        break;

                                    case "Goals Over/Under":
                                        foreach (var v in valuesEl.EnumerateArray())
                                        {
                                            string val = GetOddValueString(v, "value");
                                            string odd = GetOddValueString(v, "odd");
                                            if (val == "Over 2.5") row["Over 2.5"] = odd;
                                            else if (val == "Under 2.5") row["Under 2.5"] = odd;
                                        }
                                        break;

                                    case "Both Teams Score":
                                        foreach (var v in valuesEl.EnumerateArray())
                                        {
                                            string val = GetOddValueString(v, "value");
                                            string odd = GetOddValueString(v, "odd");
                                            if (val == "Yes") row["BTTS Sì"] = odd;
                                            else if (val == "No") row["BTTS No"] = odd;
                                        }
                                        break;

                                    case "Double Chance":
                                        foreach (var v in valuesEl.EnumerateArray())
                                        {
                                            string val = GetOddValueString(v, "value");
                                            string odd = GetOddValueString(v, "odd");
                                            if (val == "Home/Draw") row["Doppia Casa/X"] = odd;
                                            else if (val == "Home/Away") row["Doppia Casa/Trasf"] = odd;
                                            else if (val == "Draw/Away") row["Doppia X/Trasf"] = odd;
                                        }
                                        break;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            _database.LogError("elaborazione quote per singola partita", ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _database.LogError("parsing quote nella tabella", ex);
            }
        }

        /// <summary>
        /// Estrae un valore stringa da un JsonElement in modo sicuro
        /// </summary>
        private string GetOddValueString(JsonElement element, string propertyName)
        {
            try
            {
                if (!element.TryGetProperty(propertyName, out var prop))
                    return "";

                switch (prop.ValueKind)
                {
                    case JsonValueKind.String:
                        return prop.GetString() ?? "";
                    case JsonValueKind.Number:
                        return prop.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture);
                    default:
                        return prop.ToString();
                }
            }
            catch
            {
                return "";
            }
        }

        /// <summary>
        /// Elabora le risposte API non elaborate presenti nel database
        /// </summary>
        private void ProcessUnprocessedApiResponses()
        {
            try
            {
                // Recupera tutte le risposte non elaborate
                var unprocessedResponses = _apiResponseRepository.GetUnprocessedResponses();

                foreach (DataRow responseRow in unprocessedResponses.Rows)
                {
                    int responseId = Convert.ToInt32(responseRow["id"]);
                    string content = responseRow["response_content"].ToString();
                    string apiType = responseRow["api_type"].ToString();

                    try
                    {
                        // Processa il contenuto della risposta API
                        ProcessAndInsertData(content);

                        // Aggiorna lo stato della risposta come elaborata
                        _apiResponseRepository.UpdateProcessingStatus(responseId, true);
                    }
                    catch (Exception ex)
                    {
                        // Aggiorna lo stato della risposta con l'errore
                        _apiResponseRepository.UpdateProcessingStatus(responseId, false, ex.Message);
                        _database.LogError($"elaborazione risposta API ID: {responseId}", ex);
                    }
                }
            }
            catch (Exception ex)
            {
                _database.LogError("elaborazione risposte API non elaborate", ex);
                throw;
            }
        }

        /// <summary>
        /// Recupera i fixture elaborati dal database
        /// </summary>
        private DataTable GetProcessedFixturesFromDatabase()
        {
            try
            {
                // Step 1: Creare un DataTable vuoto per i fixture
                var result = CreateEmptyFixturesDataTable();
                
                // Step 2: Delegare alla classe repository appropriata il recupero dei dati
                // Utilizziamo principalmente il repository Fixture che può coordinarsi con gli altri repository
                return _fixtureRepository.GetProcessedFixtures();
            }
            catch (Exception ex)
            {
                _database.LogError("recupero partite elaborate dal database", ex);
                // Return empty table in case of error
                return CreateEmptyFixturesDataTable();
            }
        }
    }
}
