using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HorseRacingPredictor.Horses.ML;

namespace HorseRacingPredictor.Horses
{
    internal class Calculator
    {
        private readonly MachineLearningService _mlService;
        
        public Calculator()
        {
            _mlService = new MachineLearningService();
        }
        
        public int CalculateRating1(DataRow row)
        {
            /* HORSE RATING */

            // Implementa la logica per calcolare il rating del cavallo
            // Puoi usare i campi come "Handicap Rating", "Career Wins", "Jockey Last 100 Wins", ecc.
            double rating = 0;

            // Esempio di calcolo del rating (puoi personalizzarlo in base alle tue esigenze)
            rating += GetValueOrDefault(row, "Handicap Rating");
            rating += GetValueOrDefault(row, "Career Wins") * 10;
            rating += GetValueOrDefault(row, "Jockey Last 100 Wins") * 5;

            return (int)rating;
        }

        public int CalculateRating2(DataRow row)
        {
            /* AI RATING */

            // Calcola il punteggio IA basato sullo script fornito
            double rating = 0;

            rating += GetValueOrDefault(row, "Handicap Rating") * 0.3;
            rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
            rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;
            rating += GetValueOrDefault(row, "This Track Strike Rate") * 100 * 0.1;
            rating += GetValueOrDefault(row, "This Distance Strike Rate") * 100 * 0.1;
            rating += GetValueOrDefault(row, "Jockey Last 100 Strike Rate") * 100 * 0.1;
            rating += GetValueOrDefault(row, "Trainer Last 100 Strike Rate") * 100 * 0.1;

            // Limita il punteggio a un valore intero
            return (int)rating;
        }

        public int CalculateRating3(DataRow row)
        {
            /* COPILOT RATING */

            // Calcola il punteggio Copilot basato sui campi del file CSV
            double rating = 0;

            // Esempio di calcolo del rating con pesi assegnati a ciascuna statistica
            rating += GetValueOrDefault(row, "Handicap Rating") * 0.4;
            rating += GetValueOrDefault(row, "Career Wins") * 0.3;
            rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
            rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;

            return (int)rating;
        }

        public int CalculateRating4(DataRow row)
        {
            double rating = 0;

            rating += GetValueOrDefault(row, "Handicap Rating") * 0.3;
            rating += GetValueOrDefault(row, "Career Wins") * 0.2;
            rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
            rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;
            rating += GetValueOrDefault(row, "This Track Wins") * 0.1;
            rating += GetValueOrDefault(row, "This Distance Wins") * 0.1;
            rating += GetValueOrDefault(row, "Jockey Last 100 Wins") * 0.1;
            rating += GetValueOrDefault(row, "Trainer Last 100 Wins") * 0.1;

            return (int)rating;
        }

        public class RaceContext
        {
            public bool IsWetTrack { get; set; }
            public double DistanceDelta { get; set; }
            public int MaxBarrier { get; set; }
            public double AvgWeight { get; set; }
            public double MaxPrizeMoney { get; set; }
        }

        public double CalculateRating5(DataRow row)
        {
            double score = 0;

            // 1. CORE HORSE PROFILE (15%)
            // Età ottimale 4-7 anni (curva a campana)
            int age = (int)GetValueOrDefault(row, "Age");
            score += Math.Exp(-0.5 * Math.Pow((age - 5.5) / 2.0, 2)) * 0.05;

            // Gender: puledre penalizzate 2% 
            if (row["Gender"].ToString() == "f") 
                score -= 0.02;

            // 2. HISTORICAL PERFORMANCE (25%)
            double careerRuns = GetValueOrDefault(row, "Career Runs");
            double careerWins = GetValueOrDefault(row, "Career Wins");
            double careerStrike = careerRuns > 0 ? (careerWins / careerRuns) : 0;
            score += careerStrike * 0.15;

            double placingsRate = GetValueOrDefault(row, "Career Placings") / Math.Max(careerRuns, 1);
            score += placingsRate * 0.10;

            // 3. RECENT FORM (20%)
            int lastPos = (int)GetValueOrDefault(row, "Last Start Finish Position");
            score += (lastPos > 0 ? (1.0 / lastPos) : 0) * 0.10;

            double lastMargin = (int)GetValueOrDefault(row, "Last Start Margin");
            score += (1.0 / (lastMargin + 1)) * 0.05;

            //double daysSinceLast = GetDaysSinceLastRace(row); // Da implementare
            //score += Math.Exp(-daysSinceLast / 30.0) * 0.05;

            // 4. CONDITIONS SPECIALIZATION (15%)
           // bool isWet = context.IsWetTrack;
           // double surfaceRate = isWet ?
           //     GetValueOrDefault(row, "Wet Track Strike Rate") :
           ////     GetValueOrDefault(row, "Dry Track Strike Rate");
           // score += surfaceRate * 0.10;

            double distanceChange = GetValueOrDefault(row, "Last Start Distance Change");
            double distanceFit = 1.0 / (Math.Abs(distanceChange) + 1);
            score += distanceFit * 0.05;

            // 5. CONTEXT-SPECIFIC FACTORS (10%)
            double trackStrike = GetConditionalRate(row, "This Track Wins", "This Track Runs");
            score += trackStrike * 0.06;

            //double barrierEffect = (1.0 - (GetValueOrDefault(row, "Barrier") / context.MaxBarrier)) * 0.04;
            //score += barrierEffect;

            // 6. TEAM PERFORMANCE (15%)
            double jockeyRecent = GetValueOrDefault(row, "Jockey Last 100 Strike Rate");
            double jockeyLong = GetValueOrDefault(row, "Jockey 12 Months Strike Rate");
            score += Math.Max(jockeyRecent, jockeyLong) * 0.08;

            double trainerRecent = GetValueOrDefault(row, "Trainer Last 100 Strike Rate");
            double trainerLong = GetValueOrDefault(row, "Trainer 12 Months Strike Rate");
            score += Math.Max(trainerRecent, trainerLong) * 0.07;

            // 7. PHYSICAL FACTORS (10%)
           // double weightRatio = context.AvgWeight / GetValueOrDefault(row, "Weight Carried");
           // score += Math.Min(weightRatio, 1.5) * 0.05;

            double prizeMomentum = GetValueOrDefault(row, "Average Prize Money") / GetValueOrDefault(row, "Prize Money");
            score += prizeMomentum * 0.05;

            return score * 100;
        }
        
        /// <summary>
        /// Calcola il rating ML per un cavallo utilizzando il modello di machine learning
        /// </summary>
        public int CalculateMLRating(DataRow row)
        {
            try
            {
                // Prepara l'input per il modello ML
                var input = new ModelInput
                {
                    Age = GetFloatValueOrDefault(row, "Age"),
                    HandicapRating = GetFloatValueOrDefault(row, "Handicap Rating"),
                    Weight = GetFloatValueOrDefault(row, "Weight"),
                    WeightCarried = GetFloatValueOrDefault(row, "Weight Carried"),
                    Barrier = GetFloatValueOrDefault(row, "Barrier"),
                    CareerRuns = GetFloatValueOrDefault(row, "Career Runs"),
                    CareerWins = GetFloatValueOrDefault(row, "Career Wins"),
                    CareerStrikeRate = GetFloatValueOrDefault(row, "Career Strike Rate"),
                    CareerROI = GetFloatValueOrDefault(row, "Career ROI"),
                    ThisTrackRuns = GetFloatValueOrDefault(row, "This Track Runs"),
                    ThisTrackWins = GetFloatValueOrDefault(row, "This Track Wins"),
                    ThisTrackStrikeRate = GetFloatValueOrDefault(row, "This Track Strike Rate"),
                    ThisDistanceRuns = GetFloatValueOrDefault(row, "This Distance Runs"),
                    ThisDistanceWins = GetFloatValueOrDefault(row, "This Distance Wins"),
                    ThisDistanceStrikeRate = GetFloatValueOrDefault(row, "This Distance Strike Rate"),
                    JockeyLast100Wins = GetFloatValueOrDefault(row, "Jockey Last 100 Wins"),
                    JockeyLast100StrikeRate = GetFloatValueOrDefault(row, "Jockey Last 100 Strike Rate"),
                    TrainerLast100Wins = GetFloatValueOrDefault(row, "Trainer Last 100 Wins"),
                    TrainerLast100StrikeRate = GetFloatValueOrDefault(row, "Trainer Last 100 Strike Rate"),
                    BestFixedOdds = GetFloatValueOrDefault(row, "Best Fixed Odds")
                };
                
                // Effettua la previsione usando il modello ML
                var output = _mlService.PredictFinishPosition(input);
                
                // Il rating ML è inversamente proporzionale alla posizione prevista
                // Migliore è la posizione prevista (più vicina a 1), più alto sarà il rating
                double mlRating = 100 * (1.0 / Math.Max(1, output.PredictedPosition));
                
                // Aggiusta il rating in base alla probabilità di vittoria
                mlRating = mlRating * (0.6 + (output.WinProbability * 0.4));
                
                return (int)Math.Min(100, Math.Max(0, mlRating));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Errore nel calcolo del rating ML: {ex.Message}");
                
                // In caso di errore, ritorna un rating basato sul metodo standard
                return CalculateRating1(row);
            }
        }
        
        private double GetValueOrDefault(DataRow row, string columnName)
        {
            if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
                return 0;

            return Convert.ToDouble(row[columnName]);
        }
        
        private float GetFloatValueOrDefault(DataRow row, string columnName)
        {
            if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
                return 0f;

            return Convert.ToSingle(row[columnName]);
        }

        private double GetConditionalRate(DataRow row, string numeratorColumn, string denominatorColumn)
        {
            double numerator = GetValueOrDefault(row, numeratorColumn);
            double denominator = GetValueOrDefault(row, denominatorColumn);

            if (denominator == 0)
                return 0;

            return numerator / denominator;
        }
    }
}
