using CsvHelper;
using CsvHelper.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace HorseRacingPredictor.Horses
{
    /// <summary>
    /// Gestore per la lettura e l'elaborazione dei file CSV relativi alle corse dei cavalli
    /// </summary>
    public class FileReader : HorseRacingPredictor.Manager.FileReader
    {
        private readonly Manager.FileReader _fileReader;

        /// <summary>
        /// Costruttore della classe FileReader
        /// </summary>
        public FileReader()
        {
            _fileReader = new Manager.FileReader();
        }

        /// <summary>
        /// Estrae il nome del meeting dal nome del file
        /// </summary>
        /// <param name="filePath">Percorso del file</param>
        /// <returns>Nome del meeting</returns>
        public string ExtractMeetingName(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
            }

            // Ottiene solo il nome del file senza percorso
            string fileName = Path.GetFileName(filePath);

            // Cerca il pattern YYYYMMDD-meetingname-rXX.csv
            var regex = new Regex(@"^\d{8}-([a-zA-Z-]+)-r\d+\.csv$", RegexOptions.IgnoreCase);
            var match = regex.Match(fileName);

            if (match.Success && match.Groups.Count > 1)
            {
                // Estrae il nome del meeting e lo formatta
                string meetingName = match.Groups[1].Value;

                // Sostituisce i trattini con spazi e formatta ogni parola con l'iniziale maiuscola
                meetingName = string.Join(" ", meetingName.Split('-')
                    .Select(s => char.ToUpper(s[0]) + s.Substring(1).ToLower()));

                return meetingName;
            }

            // Se il pattern non corrisponde, restituisce il nome del file senza estensione
            return Path.GetFileNameWithoutExtension(filePath);
        }

        /// <summary>
        /// Estrae il numero della corsa dal nome del file
        /// </summary>
        /// <param name="filePath">Percorso del file</param>
        /// <returns>Numero della corsa o -1 se non può essere determinato</returns>
        public int ExtractRaceNumber(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
            }

            // Ottiene solo il nome del file senza percorso
            string fileName = Path.GetFileName(filePath);

            // Cerca il pattern YYYYMMDD-meetingname-rXX.csv dove XX è il numero della corsa
            var regex = new Regex(@"^\d{8}-[a-zA-Z-]+-r(\d+)\.csv$", RegexOptions.IgnoreCase);
            var match = regex.Match(fileName);

            if (match.Success && match.Groups.Count > 1)
            {
                // Estrae il numero della corsa
                if (int.TryParse(match.Groups[1].Value, out int raceNumber))
                {
                    return raceNumber;
                }
            }

            return -1; // Restituisce -1 se non è possibile determinare il numero della corsa
        }

        /// <summary>
        /// Estrae la data della corsa dal nome del file
        /// </summary>
        /// <param name="filePath">Percorso del file</param>
        /// <returns>Data della corsa o DateTime.MinValue se non può essere determinata</returns>
        public DateTime ExtractRaceDate(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
            }

            // Ottiene solo il nome del file senza percorso
            string fileName = Path.GetFileName(filePath);

            // Cerca il pattern YYYYMMDD all'inizio del nome file
            var regex = new Regex(@"^(\d{4})(\d{2})(\d{2})-", RegexOptions.IgnoreCase);
            var match = regex.Match(fileName);

            if (match.Success && match.Groups.Count > 3)
            {
                // Estrae anno, mese e giorno
                if (int.TryParse(match.Groups[1].Value, out int year) &&
                    int.TryParse(match.Groups[2].Value, out int month) &&
                    int.TryParse(match.Groups[3].Value, out int day))
                {
                    try
                    {
                        return new DateTime(year, month, day);
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        // Data non valida
                        return DateTime.MinValue;
                    }
                }
            }

            return DateTime.MinValue; // Restituisce DateTime.MinValue se non è possibile determinare la data
        }

        /// <summary>
        /// Legge i dati dei cavalli da un file CSV utilizzando CsvHelper
        /// </summary>
        /// <param name="filePath">Percorso del file CSV</param>
        /// <returns>Lista di oggetti HorseRaceData</returns>
        public List<HorseRaceData> ReadHorseDataFromFile(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
            }

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException($"File non trovato: {filePath}");
            }

            try
            {
                // Configurazione per CsvHelper che utilizza i convertitori centralizzati
                var myConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
                {
                    Delimiter = ",",
                    HasHeaderRecord = true,
                    HeaderValidated = null,
                    MissingFieldFound = null,
                    BadDataFound = null
                };

                using (var reader = new StreamReader(filePath))
                using (var csv = new CsvReader(reader, myConfig))
                {
                    // Utilizzo dei convertitori centralizzati 
                    csv.Context.TypeConverterCache.AddConverter<decimal?>(new NullableDecimalConverter());
                    csv.Context.TypeConverterCache.AddConverter<int?>(new NullableIntConverter());

                    // Registriamo la mappa di conversione tra CSV e modello
                    csv.Context.RegisterClassMap<Punters>();

                    // Leggi i record
                    var records = csv.GetRecords<HorseRaceData>().ToList();

                    // Stampa il numero di record letti
                    Console.WriteLine($"Letti {records.Count} record dal file {Path.GetFileName(filePath)}");

                    return records;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Errore durante la lettura del file CSV {filePath}: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// Ottiene tutti i file CSV relativi alle corse dei cavalli in una cartella
        /// </summary>
        /// <param name="folderPath">Percorso della cartella</param>
        /// <returns>Lista di percorsi dei file CSV</returns>
        public List<string> GetHorseRaceFiles(string folderPath)
        {
            return _fileReader.GetCsvFiles(folderPath);
        }

        /// <summary>
        /// Ottiene informazioni sulla corsa dal nome del file
        /// </summary>
        /// <param name="filePath">Percorso del file</param>
        /// <returns>Tupla contenente (NomeMeeting, NumeroCorsa, DataCorsa)</returns>
        public (string MeetingName, int RaceNumber, DateTime RaceDate) GetRaceInfoFromFileName(string filePath)
        {
            string meetingName = ExtractMeetingName(filePath);
            int raceNumber = ExtractRaceNumber(filePath);
            DateTime raceDate = ExtractRaceDate(filePath);

            return (meetingName, raceNumber, raceDate);
        }

        /// <summary>
        /// Metodo per processare tutti i file di corse in una cartella
        /// </summary>
        /// <param name="folderPath">Percorso della cartella contenente i file CSV</param>
        /// <returns>Dictionary con chiave = identificativo corsa, valore = lista di dati cavalli</returns>
        public Dictionary<string, List<HorseRaceData>> ProcessAllRaceFiles(string folderPath)
        {
            var result = new Dictionary<string, List<HorseRaceData>>();
            var files = GetHorseRaceFiles(folderPath);

            Console.WriteLine($"Trovati {files.Count} file CSV nella cartella {folderPath}");

            foreach (var file in files)
            {
                try
                {
                    Console.WriteLine($"Elaborazione file: {Path.GetFileName(file)}");

                    var horseDataList = ReadHorseDataFromFile(file);
                    var raceInfo = GetRaceInfoFromFileName(file);

                    string key = $"{raceInfo.RaceDate:yyyy-MM-dd} - {raceInfo.MeetingName} - Race {raceInfo.RaceNumber}";
                    result.Add(key, horseDataList);

                    Console.WriteLine($"Elaborati {horseDataList.Count} record per la corsa {key}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Errore nel processare il file {file}: {ex.Message}");
                }
            }

            Console.WriteLine($"Totale corse elaborate: {result.Count}");
            return result;
        }
    }
}
