using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Text.Json;
using ConsoleTableExt;
using LibraryPricer;
using Microsoft.SqlServer.Server;
using System.Data;
using System.Diagnostics;
using static LibraryPricer.CalcFunctions;
using MathNet.Numerics.Optimization;
using System.Reflection;
using MathNet.Numerics;
using MathNet.Numerics.Statistics;
using LibraryPricer.Payoffs;
using Accord.Math;

namespace PricerAppLite // Note: actual namespace depends on the project name.
{
    internal class ProgramLite
    {
        static void Main(string[] args)
        {
            string version = "2.2 - [12/06/2025]";
            Console.Title = $"Pricer v.{version}";
            ManualRun(args);
        }
        public static void ManualRun(string[] args)
        {
            bool showSimulationDebug = args.Any(a => a.Equals("--sim-debug", StringComparison.OrdinalIgnoreCase));

            // Input Isin da elaborare
            Console.WriteLine("Inserire ISIN (default IT0006767633): "); // CH0456761649 (Airbag) ,	XS2306677134 (Sigma), IT0006746074 (Relief), CH0392531056 (TwinWin)
            string? Isin = Console.ReadLine();
            if (String.IsNullOrEmpty(Isin)) Isin = "IT0006767633";

            // Input numero prezzi eod per calcolo matrice correlazione
            Console.WriteLine("Num. Prezzi EOD per calcolo matrice correlazione e volatilità (default 252): "); 
            string? PrezziEOD = Console.ReadLine();
            int numPrezziEOD = 0;
            if (String.IsNullOrEmpty(PrezziEOD)) numPrezziEOD = 252;
            else numPrezziEOD = Convert.ToInt32(PrezziEOD);

            // Carica dati da API
            PricerApiResponse apiData = LoadApiData(Isin, numPrezziEOD);

            // Verifica Isin sia valido per il pricer
            if (!string.IsNullOrEmpty(apiData.ISINCheck) && apiData.ISINCheck == "KO")
            {
                Console.WriteLine($"ISIN {Isin} non processabile!");
                Console.WriteLine("Certificati nel pricer devono avere le seguenti caratteristiche : ");
                Console.WriteLine("- Categoria: Coupon,StepUp,Bonus (con Barriera Discreta)");
                Console.WriteLine("- Status: In Quotazione");
                Console.WriteLine("- Direction: Long");
                Console.WriteLine("- Basket Type : Worst");
                Console.WriteLine("- Currency : EUR");
                Console.WriteLine("- CedolaVariabile : FALSE");
                Console.WriteLine("- Darwin : FALSE");
                Console.WriteLine("- Domino : FALSE");
                Console.WriteLine("- Magnet : FALSE");
                RestartOrExit(args);
            }

            Console.WriteLine("Considerare i dividendi ? s/n (default s): ");
            string? IsDividend = Console.ReadLine();
            if (String.IsNullOrEmpty(IsDividend)) IsDividend = "s";
            
            int num_sims = 0;
            Console.WriteLine("Inserire # simulazioni montecarlo (default = 10000) : ");
            string? numsim = Console.ReadLine();
            if (String.IsNullOrEmpty(numsim)) num_sims = 10000;
            else num_sims = Convert.ToInt32(numsim);

            // Carica dettagli sottostanti
            var detailsULModel = apiData.DetailsUL?.ToArray() ?? Array.Empty<DetailsULItem>();

            if (detailsULModel.Length == 0)
            {
                Console.WriteLine("Nessun dettaglio sottostante disponibile.");
                RestartOrExit(args);
            }

            // Calcolo matrice di correlazione e volatilita da prezzi EOD
            var pricesById = BuildPriceSeries(apiData.HistPriceUL, numPrezziEOD);
            double[,] corrMatrix = BuildCorrelationMatrix(detailsULModel, pricesById, numPrezziEOD);
            double[] Volatility = BuildVolatility(detailsULModel, pricesById, numPrezziEOD);

            // Visualizza matrice di correlazione in Console
            string[] NomiSottostanti = detailsULModel.AsEnumerable().Select(r => r.Sottostante).ToArray();
            ConsoleTableBuilder.From(CalcFunctions.ArraytoDatatable(corrMatrix, NomiSottostanti))
                               .WithTitle($"Matrix Correl {Isin}", ConsoleColor.Yellow, ConsoleColor.DarkGray)
            .ExportAndWriteLine();

            // Carica dettagli certificato 
            var detailsCTFModel = apiData.DetailsCTF?.ToArray() ?? Array.Empty<DetailsCTFItem>();
            if (detailsCTFModel.Length == 0)
            {
                Console.WriteLine("Nessun dettaglio certificato disponibile.");
                RestartOrExit(args);
            }

            // Carica dettagli eventi 
            var detailsEventModel = apiData.DetailsEvent?.ToArray() ?? Array.Empty<DetailsEventItem>();
            var detailsEventExDateModel = apiData.DetailsEventExDate?.ToArray() ?? Array.Empty<DetailsEventExDateItem>();

            // Inizializzazione parametri input per calcolo FairValue da detailsCTFModel
            double TassoInteresse = detailsCTFModel.AsEnumerable().Select(r => r.TassoInteresse).FirstOrDefault();
            DateTime baseDate = detailsCTFModel.AsEnumerable().Select(r => r.LastDatePrice).FirstOrDefault().Date;
            DateTime maturityDate = detailsCTFModel.AsEnumerable().Select(r => r.Maturity_Date).FirstOrDefault().Date;
            int DaysToMaturity = CountBusinessDaysInclusive(baseDate, maturityDate);
            string? PDI_Style = detailsCTFModel.AsEnumerable().Select(r => r.PDI_Style).FirstOrDefault();
            double PDI_Strike = detailsCTFModel.AsEnumerable().Select(r => r.PDI_Strike).FirstOrDefault();
            double PDI_Barrier = Math.Round(detailsCTFModel.AsEnumerable().Select(r => r.PDI_Barrier).FirstOrDefault(), 4);
            double CapitalValue = detailsCTFModel.AsEnumerable().Select(r => r.CapitalValue).FirstOrDefault();
            double NominalAmount = detailsCTFModel.AsEnumerable().Select(r => r.NominalAmount).FirstOrDefault();
            double Bid = detailsCTFModel.AsEnumerable().Select(r => r.Bid).FirstOrDefault();
            double Ask = detailsCTFModel.AsEnumerable().Select(r => r.Ask).FirstOrDefault();
            DateTime LastDatePrice = detailsCTFModel.AsEnumerable().Select(r => r.LastDatePrice).FirstOrDefault();
            double CouponInMemory = detailsCTFModel.AsEnumerable().Select(r => r.CouponInMemory).FirstOrDefault();
            double ProtMinVal = detailsCTFModel.AsEnumerable().Select(r => r.ProtMinVal).FirstOrDefault();
            int AirBag = detailsCTFModel.AsEnumerable().Select(r => r.AirBag ? 1 : 0).FirstOrDefault();
            double FattoreAirbag = detailsCTFModel.AsEnumerable().Select(r => r.FattoreAirbag).FirstOrDefault();
            int OneStar = detailsCTFModel.AsEnumerable().Select(r => r.OneStar ? 1 : 0).FirstOrDefault();
            double TriggerOnestar = Math.Round(detailsCTFModel.AsEnumerable().Select(r => r.TriggerOnestar).FirstOrDefault(),4);
            int TwinWin = detailsCTFModel.AsEnumerable().Select(r => r.TwinWin ? 1 : 0).FirstOrDefault();
            int Sigma = detailsCTFModel.AsEnumerable().Select(r => r.Sigma ? 1 : 0).FirstOrDefault();
            int Relief = detailsCTFModel.AsEnumerable().Select(r => r.Relief ? 1 : 0).FirstOrDefault();
            int Domino = detailsCTFModel.AsEnumerable().Select(r => r.Domino ? 1 : 0).FirstOrDefault();
            string Category = detailsCTFModel.AsEnumerable().Select(r => r.Category).FirstOrDefault();
            double CAP = detailsCTFModel.AsEnumerable().Select(r => r.CAP ?? 0.0).FirstOrDefault();
            double Leva = detailsCTFModel.AsEnumerable().Select(r => r.Leva ?? 0.0).FirstOrDefault();

            // Inizializzazione parametri input per calcolo FairValue da detailsULModel
            double[] PricesUL = detailsULModel.AsEnumerable().Select(r => r.SpotPriceNormalized).ToArray();
            double[] Dividends = detailsULModel.AsEnumerable().Select(r => r.Dividend).ToArray();
            if (IsDividend != "s") System.Array.Clear(Dividends, 0, Dividends.Length); // non usa i dividendi se non richiesto
            //double[] Volatility = detailsULModel.AsEnumerable().Select(r => r.Volatility).ToArray();
            int numSottostanti = detailsULModel.Length;

            // Inizializzazione parametri input per calcolo FairValue da detailsEventModel
            int[] DaysToObs = detailsEventModel.Select(r => CountBusinessDaysInclusive(baseDate, r.ObsDate.Date)).ToArray();
            int[] DaysToExDate = detailsEventExDateModel.Select(r => CountBusinessDaysInclusive(baseDate, r.ExDate.Date)).ToArray();
            double[] DaysToObsYFract = DaysToObs.Select(r => r / 252.0).ToArray();
            double[] DaysToExDateYFract = DaysToExDate.Select(r => r / 252.0).ToArray();
            double[] CouponValues = detailsEventModel.Select(r => r.CouponValue).ToArray();
            double[] CouponTriggers = detailsEventModel.Select(r => r.CouponTrigger).ToArray();
            double[] AutocallValues = detailsEventModel.Select(r => r.AutocallValue).ToArray();
            double[] AutocallTriggers = detailsEventModel.Select(r => r.AutocallTrigger).ToArray();
            int[] MemoryFlags = detailsEventModel.Select(r => r.Memory ? 1 : 0).ToArray();
            int numEventi = detailsEventModel.Length;

            // Visualizza dati input Sottostanti
            DataTable dtInputDataUL = new DataTable();
            dtInputDataUL.Columns.Add("Sottostante", typeof(string));
            dtInputDataUL.Columns.Add("Price", typeof(double));
            dtInputDataUL.Columns.Add("Dividend", typeof(double));
            dtInputDataUL.Columns.Add("Volatility", typeof(double));

            for (int i = 0; i < numSottostanti; i++)
            {
                dtInputDataUL.Rows.Add(
                    NomiSottostanti[i],
                    Math.Round(PricesUL[i], 4),
                    Math.Round(Dividends[i], 4),
                    Math.Round(Volatility[i], 4)
                );
            }

            Console.WriteLine();
            ConsoleTableBuilder.From(dtInputDataUL)
                               .WithTitle($"Dati Input Sottostanti {Isin}", ConsoleColor.Cyan, ConsoleColor.DarkGray)
                               .ExportAndWriteLine();

            // Visualizza dati input Certificato
            DataTable dtInputDataCTF = new DataTable();
            dtInputDataCTF.Columns.Add("TassoInteresse", typeof(double));
            dtInputDataCTF.Columns.Add("DaysToMaturity", typeof(double));
            dtInputDataCTF.Columns.Add("PDI Style", typeof(string));
            dtInputDataCTF.Columns.Add("PDI Barrier", typeof(double));
            dtInputDataCTF.Columns.Add("Capital Value", typeof(double));
            dtInputDataCTF.Columns.Add("Coupon In Memoria", typeof(double));
            dtInputDataCTF.Columns.Add("Prot Min Val %", typeof(double));
            dtInputDataCTF.Rows.Add(Math.Round(TassoInteresse, 4), DaysToMaturity, PDI_Style, PDI_Barrier, Math.Round(CapitalValue * NominalAmount, 5), Math.Round(CouponInMemory, 5), Math.Round(ProtMinVal, 3));
            Console.WriteLine();
            ConsoleTableBuilder.From(dtInputDataCTF)
                               .WithTitle($"Dati Input Certificato {Isin}", ConsoleColor.Green, ConsoleColor.DarkGray)
            .ExportAndWriteLine();

            // Visualizza dati input Flag Certificato
            DataTable dtInputFlagCTF = new DataTable();
            dtInputFlagCTF.Columns.Add("AirBag", typeof(int));
            dtInputFlagCTF.Columns.Add("Fattore Airbag", typeof(double));
            dtInputFlagCTF.Columns.Add("OneStar", typeof(int));
            dtInputFlagCTF.Columns.Add("TriggerOnestar", typeof(double));
            dtInputFlagCTF.Columns.Add("TwinWin", typeof(int));
            dtInputFlagCTF.Columns.Add("Sigma", typeof(int));
            dtInputFlagCTF.Columns.Add("Relief", typeof(int));
            //dtInputFlagCTF.Columns.Add("Domino", typeof(int));
            dtInputFlagCTF.Rows.Add(AirBag, Math.Round(FattoreAirbag, 3), OneStar, TriggerOnestar, TwinWin, Sigma, Relief);
            Console.WriteLine();
            ConsoleTableBuilder.From(dtInputFlagCTF)
                               .WithTitle($"Dati Input Flags {Isin}", ConsoleColor.DarkCyan, ConsoleColor.DarkGray)
            .ExportAndWriteLine();


            // Visualizza dati input Eventi
            DataTable dtInputDataEvents = new DataTable();
            dtInputDataEvents.Columns.Add("DaysToObs.", typeof(int));
            //dtInputDataEvents.Columns.Add("DaysToExDate", typeof(int)); // Solo Debug nuova colonna
            dtInputDataEvents.Columns.Add("DaysToObsYFract", typeof(double));
            dtInputDataEvents.Columns.Add("Cpn. Value", typeof(double));
            dtInputDataEvents.Columns.Add("Cpn. Trigger", typeof(double));
            dtInputDataEvents.Columns.Add("Autoc. Value", typeof(double));
            dtInputDataEvents.Columns.Add("Autoc. Trigger", typeof(double));
            dtInputDataEvents.Columns.Add("Memory", typeof(int));

            for (int i = 0; i < numEventi; i++)
            {
                int exDate = (i < DaysToExDate.Length) ? DaysToExDate[i] : -1;

                dtInputDataEvents.Rows.Add(
                    DaysToObs[i],
                    //exDate,
                    Math.Round(DaysToObsYFract[i], 4),
                    Math.Round(CouponValues[i] * NominalAmount, 6),
                    Math.Round(CouponTriggers[i], 6),
                    Math.Round(AutocallValues[i] * NominalAmount, 6),
                    Math.Round(AutocallTriggers[i], 6),
                    MemoryFlags[i]
                );
            }

            Console.WriteLine();
            ConsoleTableBuilder.From(dtInputDataEvents)
                               .WithTitle($"Dati Input Eventi {Isin}", ConsoleColor.DarkYellow, ConsoleColor.DarkGray)
                               .ExportAndWriteLine();

            Console.WriteLine("Elaborazione Fair value in corso...");

            // Costruzione del contesto originale dai dati caricati
            var contextOriginal = new PayoffContext
            {
                DaysToMaturity = DaysToMaturity,
                r = TassoInteresse,
                CapitalValue = CapitalValue,
                ProtMinVal = ProtMinVal,
                PDI_Barrier = PDI_Barrier,
                PDI_Style = PDI_Style,
                CouponInMemory = CouponInMemory,
                DaysToObsYFract = DaysToObsYFract,
                CouponTriggers = CouponTriggers,
                CouponValues = CouponValues,
                AutocallTriggers = AutocallTriggers,
                AutocallValues = AutocallValues,
                MemoryFlags = MemoryFlags,
                Caso = GetCasoString(AirBag, Sigma, Relief, TwinWin, OneStar),
                PDI_Strike = PDI_Strike,
                FattoreAirbag = FattoreAirbag,
                TriggerOneStar = TriggerOnestar,
                CAP = CAP,
                DaysToObs = DaysToObs,
                Airbag = AirBag,
                Sigma = Sigma,
                Relief = Relief,
                TwinWin = TwinWin,
                OneStar = OneStar,
                Leva = Leva,
                Volatility = Volatility,
                PricesUL = PricesUL,
                NominalAmount = NominalAmount,
                EnableDebug = showSimulationDebug
            };

            // Cloni distinti per ogni motore
            var contextForFairValue = FairValueComparison.CloneContext(contextOriginal);
            var contextForArray = FairValueComparison.CloneContext(contextOriginal);
            var contextForCompare = FairValueComparison.CloneContext(contextOriginal);

            // Run FairValueArray per distribuzione
            double[] fvalues = CalcFunctions.FairValueArray(
                PricesUL,
                corrMatrix,
                numSottostanti,
                num_sims,
                TassoInteresse,
                DaysToMaturity,
                Dividends,
                Volatility,
                DaysToObs,
                DaysToObsYFract,
                CouponValues,
                CouponTriggers,
                AutocallValues,
                AutocallTriggers,
                MemoryFlags,
                CouponInMemory,
                PDI_Style,
                PDI_Strike,
                PDI_Barrier,
                CapitalValue,
                ProtMinVal,
                AirBag,
                Sigma,
                TwinWin,
                Relief,
                FattoreAirbag,
                OneStar,
                TriggerOnestar,
                CAP,
                Leva).FairValueArray;

            int[] fv = new int[fvalues.Length];
            for (int i = 0; i < fvalues.Length; i++)
                fv[i] = (int)Math.Floor(fvalues[i] * NominalAmount / 100);


            var stopwatch = System.Diagnostics.Stopwatch.StartNew();

            // Run GBM con Engine Payoff
            FairValueComparison.CompareFairValues(
                num_sims,
                Enumerable.Range(0, num_sims)
                  .Select(_ => (double[])PricesUL.Clone())
                  .ToArray(),
                corrMatrix,
                numSottostanti,
                DaysToMaturity,
                TassoInteresse,
                Dividends,
                contextForCompare,
                NominalAmount,
                Isin,
                Ask,
                Bid,
                LastDatePrice,
                Category,
                detailsEventModel.Select(r => r.ObsDate.Date).ToArray()
            );

            stopwatch.Stop();
            Console.WriteLine($"\nTempo Elaborazione GBM Engine Payoff: {stopwatch.Elapsed.TotalSeconds:F2} sec");
            Console.WriteLine("----------------------------------------");

            // Verifica potenziale sottostima del fair value se ci sono ex-date future non ancora maturate
            if (DaysToExDate.Length > DaysToObs.Length)
            {
                int lastObs = DaysToObs.Last();
                int lastEx = DaysToExDate.Last();

                if (lastObs < lastEx)
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("ATTENZIONE: POSSIBILE SOTTOSTIMA DEL FAIR VALUE");

                    Console.WriteLine("\n Il calcolo del fair value presume che il coupon sia già stato pagato,");
                    Console.WriteLine("   ma in realtà non è ancora maturato (la Ex-Date non è ancora stata raggiunta).");
                    Console.WriteLine("   Il titolo incorpora ancora quel coupon nel suo prezzo, ma il pricer lo ha già scontato.");

                    Console.WriteLine("\n Inoltre, se erano presenti coupon in memoria, questi sono stati azzerati nel calcolo,");
                    Console.WriteLine("   come se il pagamento fosse già avvenuto.");
                    Console.WriteLine("   In realtà il diritto al pagamento non è ancora stato acquisito.");
                    Console.WriteLine("   Questo comporta una POSSIBILE SOTTOSTIMA SIGNIFICATIVA del FAIR VALUE.");

                    Console.WriteLine("\n SUGGERIMENTO: verificare manualmente l’ultimo evento osservato");
                    Console.WriteLine("   e valutare l’impatto potenziale sul fair value atteso.");
                    Console.ResetColor();
                }
               
            }
            RestartOrExit(args);

        }

        private static PricerApiResponse LoadApiData(string isin, int numPrezziEOD)
        {
            string url = $"https://smartapi.smart-roots.net/api/Pricer?isin={Uri.EscapeDataString(isin)}&numPrezziEOD={numPrezziEOD}";
            using HttpClient httpClient = new HttpClient();
            httpClient.Timeout = TimeSpan.FromSeconds(60);
            string json = httpClient.GetStringAsync(url).GetAwaiter().GetResult();
            var options = new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            };

            PricerApiResponse? data = JsonSerializer.Deserialize<PricerApiResponse>(json, options);
            if (data == null)
            {
                throw new InvalidOperationException("Risposta API non valida.");
            }

            return data;
        }

        private static Dictionary<int, double[]> BuildPriceSeries(List<HistPriceULItem>? histPrice, int numPrezziEOD)
        {
            if (histPrice == null || histPrice.Count == 0)
            {
                return new Dictionary<int, double[]>();
            }

            var byUl = histPrice
                .GroupBy(p => p.IDUnderlyings)
                .ToDictionary(
                    g => g.Key,
                    g => g.OrderBy(p => p.rn).Select(p => p.Px_close).Take(numPrezziEOD).ToArray()
                );

            return byUl;
        }

        private static double[,] BuildCorrelationMatrix(DetailsULItem[] detailsULModel, Dictionary<int, double[]> pricesById, int numPrezziEOD)
        {
            int numUL = detailsULModel.Length;
            double[,] correlMatrix = new double[numUL, numUL];

            int expected = numUL * numPrezziEOD;
            int actual = pricesById.Values.Sum(p => p.Length);
            if (expected > 0 && actual < expected)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine($"Attenzione! Rilevati meno di {numPrezziEOD} prezzi eod in uno dei sottostanti");
                Console.WriteLine($"Matrice di correlazione calcolata usando {actual / Math.Max(1, numUL)} prezzi eod");
                Console.ResetColor();
            }

            for (int i = 0; i < numUL; i++)
            {
                correlMatrix[i, i] = 1.0;
                int idA = detailsULModel[i].IDUnderlyings;
                double[] seriesA = pricesById.ContainsKey(idA) ? pricesById[idA] : Array.Empty<double>();

                for (int j = i + 1; j < numUL; j++)
                {
                    int idB = detailsULModel[j].IDUnderlyings;
                    double[] seriesB = pricesById.ContainsKey(idB) ? pricesById[idB] : Array.Empty<double>();

                    int len = Math.Min(seriesA.Length, seriesB.Length);
                    double corr = 0.0;
                    if (len >= 2)
                    {
                        corr = Correlation.Pearson(seriesA.Take(len), seriesB.Take(len));
                    }
                    correlMatrix[i, j] = corr;
                }

                for (int k = 0; k < numUL; k++)
                {
                    if (correlMatrix[i, k] == 0) correlMatrix[i, k] = correlMatrix[k, i];
                }
            }

            return correlMatrix;
        }

        private static double[] BuildVolatility(DetailsULItem[] detailsULModel, Dictionary<int, double[]> pricesById, int numPrezziEOD)
        {
            int numUL = detailsULModel.Length;
            double[] volatilities = new double[numUL];

            for (int i = 0; i < numUL; i++)
            {
                int id = detailsULModel[i].IDUnderlyings;
                double[] prices = pricesById.ContainsKey(id) ? pricesById[id] : Array.Empty<double>();
                volatilities[i] = ComputeAnnualizedVolatility(prices);
            }

            int expected = numUL * numPrezziEOD;
            int actual = pricesById.Values.Sum(p => p.Length);
            if (expected > 0 && actual < expected)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine($"Volatilita calcolata usando {actual / Math.Max(1, numUL)} prezzi eod");
                Console.ResetColor();
            }

            return volatilities;
        }

        private static double ComputeAnnualizedVolatility(double[] prices)
        {
            if (prices == null || prices.Length < 2)
            {
                return 0.0;
            }

            int n = prices.Length;
            double[] logReturns = new double[n - 1];
            for (int i = 0; i < n - 1; i++)
            {
                if (prices[i] > 0 && prices[i + 1] > 0)
                {
                    logReturns[i] = Math.Log(prices[i] / prices[i + 1]);
                }
                else
                {
                    logReturns[i] = 0.0;
                }
            }

            double avg = logReturns.Average();
            double sumSquared = logReturns.Select(r => Math.Pow(r - avg, 2)).Sum();
            double variance = logReturns.Length > 1 ? sumSquared / (logReturns.Length - 1) : 0.0;
            double stdDev = Math.Sqrt(variance);
            return stdDev * Math.Sqrt(252);
        }

        private static int CountBusinessDaysInclusive(DateTime startDate, DateTime endDate)
        {
            if (endDate < startDate)
            {
                return 0;
            }

            int count = 0;
            DateTime current = startDate;
            while (current <= endDate)
            {
                if (current.DayOfWeek != DayOfWeek.Saturday && current.DayOfWeek != DayOfWeek.Sunday)
                {
                    count++;
                }
                current = current.AddDays(1);
            }

            return count;
        }

        private class PricerApiResponse
        {
            public string ISINCheck { get; set; }
            public List<HistPriceULItem> HistPriceUL { get; set; }
            public List<DetailsULItem> DetailsUL { get; set; }
            public List<DetailsEventItem> DetailsEvent { get; set; }
            public List<DetailsEventExDateItem> DetailsEventExDate { get; set; }
            public List<DetailsCTFItem> DetailsCTF { get; set; }
        }

        private class HistPriceULItem
        {
            public int IDUnderlyings { get; set; }
            public string Sottostante { get; set; }
            public double Px_close { get; set; }
            public DateTime Px_date { get; set; }
            public int rn { get; set; }
        }

        private class DetailsULItem
        {
            public int IDUnderlyings { get; set; }
            public string Sottostante { get; set; }
            public double LastPrice { get; set; }
            public double Strike { get; set; }
            public double SpotPriceNormalized { get; set; }
            public double Dividend { get; set; }
            public double Volatility { get; set; }
            public double TassoInteresse { get; set; }
        }

        private class DetailsEventItem
        {
            public DateTime ObsDate { get; set; }
            public double CouponValue { get; set; }
            public double CouponTrigger { get; set; }
            public double AutocallValue { get; set; }
            public double AutocallTrigger { get; set; }
            public bool Memory { get; set; }
        }

        private class DetailsEventExDateItem
        {
            public DateTime ExDate { get; set; }
            public double CouponValue { get; set; }
            public double CouponTrigger { get; set; }
            public double AutocallValue { get; set; }
            public double AutocallTrigger { get; set; }
            public bool Memory { get; set; }
        }

        private class DetailsCTFItem
        {
            public double TassoInteresse { get; set; }
            public DateTime Maturity_Date { get; set; }
            public string PDI_Style { get; set; }
            public double PDI_Strike { get; set; }
            public double PDI_Barrier { get; set; }
            public double CapitalValue { get; set; }
            public double NominalAmount { get; set; }
            public double Bid { get; set; }
            public double Ask { get; set; }
            public DateTime LastDatePrice { get; set; }
            public double CouponInMemory { get; set; }
            public double ProtMinVal { get; set; }
            public bool AirBag { get; set; }
            public double FattoreAirbag { get; set; }
            public bool OneStar { get; set; }
            public double TriggerOnestar { get; set; }
            public bool TwinWin { get; set; }
            public bool Sigma { get; set; }
            public bool Relief { get; set; }
            public bool Domino { get; set; }
            public string Category { get; set; }
            public double? CAP { get; set; }
            public double? Leva { get; set; }
        }

        private static string GetCasoString(int airbag, int sigma, int relief, int twinwin, int onestar)
        {
            List<string> componenti = new();

            if (airbag == 1) componenti.Add("Airbag");
            if (sigma == 1) componenti.Add("Sigma");
            if (relief == 1) componenti.Add("Relief");
            if (twinwin == 1) componenti.Add("TwinWin");
            if (onestar == 1) componenti.Add("OneStar");

            return componenti.Count > 0 ? string.Join(" + ", componenti) : "Standard";
        }
        private static void RestartOrExit(string[] args)
        {
            Console.WriteLine();
            Console.WriteLine("Premere Invio per riavviare il programma o Q per uscire...");
            string? input = Console.ReadLine();

            if (!string.IsNullOrWhiteSpace(input) &&
                input.Equals("Q", StringComparison.OrdinalIgnoreCase))
            {
                Environment.Exit(0);
            }

            if (!Console.IsOutputRedirected)
            {
                try
                {
                    Console.Clear();
                }
                catch (IOException)
                {
                    // Ignore if console buffer isn't available (e.g., redirected output).
                }
            }

            ManualRun(args);
        }
    }
}
