using System;
using System.Collections.Generic;
using System.Linq;
using NT8.Core.Intelligence;
using NT8.Core.Logging;

namespace NT8.Core.Analytics
{
    /// <summary>
    /// Factor-level analysis report.
    /// </summary>
    public class FactorAnalysisReport
    {
        public FactorType Factor { get; set; }
        public double CorrelationToPnL { get; set; }
        public double Importance { get; set; }
        public Dictionary<string, double> BucketWinRate { get; set; }
        public Dictionary<string, double> BucketAvgPnL { get; set; }

        public FactorAnalysisReport()
        {
            BucketWinRate = new Dictionary<string, double>();
            BucketAvgPnL = new Dictionary<string, double>();
        }
    }

    /// <summary>
    /// Validates confluence score quality and recommends weight adjustments.
    /// </summary>
    public class ConfluenceValidator
    {
        private readonly ILogger _logger;

        public ConfluenceValidator(ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
        }

        /// <summary>
        /// Analyzes one factor against trade outcomes.
        /// </summary>
        public FactorAnalysisReport AnalyzeFactor(FactorType factor, List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var report = new FactorAnalysisReport();
                report.Factor = factor;

                var values = ExtractFactorValues(factor, trades);
                report.CorrelationToPnL = Correlation(values, trades.Select(t => t.RealizedPnL).ToList());
                report.Importance = Math.Abs(report.CorrelationToPnL);

                var low = new List<int>();
                var medium = new List<int>();
                var high = new List<int>();

                for (var i = 0; i < values.Count; i++)
                {
                    var v = values[i];
                    if (v < 0.5)
                        low.Add(i);
                    else if (v < 0.8)
                        medium.Add(i);
                    else
                        high.Add(i);
                }

                AddBucket(report, "Low", low, trades);
                AddBucket(report, "Medium", medium, trades);
                AddBucket(report, "High", high, trades);

                return report;
            }
            catch (Exception ex)
            {
                _logger.LogError("AnalyzeFactor failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Estimates factor importance values normalized to 1.0.
        /// </summary>
        public Dictionary<FactorType, double> CalculateFactorImportance(List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var result = new Dictionary<FactorType, double>();
                var raw = new Dictionary<FactorType, double>();
                var total = 0.0;

                var supported = new[]
                {
                    FactorType.Setup,
                    FactorType.Trend,
                    FactorType.Volatility,
                    FactorType.Timing,
                    FactorType.ExecutionQuality
                };

                foreach (var factor in supported)
                {
                    var analysis = AnalyzeFactor(factor, trades);
                    var score = Math.Max(0.0001, analysis.Importance);
                    raw.Add(factor, score);
                    total += score;
                }

                foreach (var kvp in raw)
                {
                    result.Add(kvp.Key, kvp.Value / total);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError("CalculateFactorImportance failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Recommends confluence weights based on observed importance.
        /// </summary>
        public Dictionary<FactorType, double> RecommendWeights(List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var importance = CalculateFactorImportance(trades);
                return importance;
            }
            catch (Exception ex)
            {
                _logger.LogError("RecommendWeights failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Validates whether score implies expected outcome.
        /// </summary>
        public bool ValidateScore(ConfluenceScore score, TradeOutcome outcome)
        {
            if (score == null)
                throw new ArgumentNullException("score");

            try
            {
                if (score.WeightedScore >= 0.7)
                    return outcome == TradeOutcome.Win;
                if (score.WeightedScore <= 0.4)
                    return outcome == TradeOutcome.Loss;

                return outcome != TradeOutcome.Breakeven;
            }
            catch (Exception ex)
            {
                _logger.LogError("ValidateScore failed: {0}", ex.Message);
                throw;
            }
        }

        private static void AddBucket(FactorAnalysisReport report, string bucket, List<int> indices, List<TradeRecord> trades)
        {
            if (indices.Count == 0)
            {
                report.BucketWinRate[bucket] = 0.0;
                report.BucketAvgPnL[bucket] = 0.0;
                return;
            }

            var selected = indices.Select(i => trades[i]).ToList();
            report.BucketWinRate[bucket] = (double)selected.Count(t => t.RealizedPnL > 0.0) / selected.Count;
            report.BucketAvgPnL[bucket] = selected.Average(t => t.RealizedPnL);
        }

        private static List<double> ExtractFactorValues(FactorType factor, List<TradeRecord> trades)
        {
            var values = new List<double>();
            foreach (var trade in trades)
            {
                switch (factor)
                {
                    case FactorType.Setup:
                        values.Add(trade.ConfluenceScore);
                        break;
                    case FactorType.Trend:
                        values.Add(TrendScore(trade.TrendRegime));
                        break;
                    case FactorType.Volatility:
                        values.Add(VolatilityScore(trade.VolatilityRegime));
                        break;
                    case FactorType.Timing:
                        values.Add(TimingScore(trade.EntryTime));
                        break;
                    case FactorType.ExecutionQuality:
                        values.Add(ExecutionQualityScore(trade));
                        break;
                    default:
                        values.Add(0.5);
                        break;
                }
            }

            return values;
        }

        private static double TrendScore(TrendRegime trend)
        {
            switch (trend)
            {
                case TrendRegime.StrongUp:
                case TrendRegime.StrongDown:
                    return 0.9;
                case TrendRegime.WeakUp:
                case TrendRegime.WeakDown:
                    return 0.7;
                default:
                    return 0.5;
            }
        }

        private static double VolatilityScore(VolatilityRegime volatility)
        {
            switch (volatility)
            {
                case VolatilityRegime.Low:
                case VolatilityRegime.BelowNormal:
                    return 0.8;
                case VolatilityRegime.Normal:
                    return 0.6;
                case VolatilityRegime.Elevated:
                    return 0.4;
                default:
                    return 0.2;
            }
        }

        private static double TimingScore(DateTime timestamp)
        {
            var t = timestamp.TimeOfDay;
            if (t < new TimeSpan(10, 30, 0))
                return 0.8;
            if (t < new TimeSpan(14, 0, 0))
                return 0.5;
            if (t < new TimeSpan(16, 0, 0))
                return 0.7;
            return 0.3;
        }

        private static double ExecutionQualityScore(TradeRecord trade)
        {
            if (trade.StopTicks <= 0)
                return 0.5;

            var scaled = trade.RMultiple / 3.0;
            if (scaled < 0.0)
                scaled = 0.0;
            if (scaled > 1.0)
                scaled = 1.0;
            return scaled;
        }

        private static double Correlation(List<double> xs, List<double> ys)
        {
            if (xs.Count != ys.Count || xs.Count < 2)
                return 0.0;

            var xAvg = xs.Average();
            var yAvg = ys.Average();
            var sumXY = 0.0;
            var sumXX = 0.0;
            var sumYY = 0.0;

            for (var i = 0; i < xs.Count; i++)
            {
                var dx = xs[i] - xAvg;
                var dy = ys[i] - yAvg;
                sumXY += dx * dy;
                sumXX += dx * dx;
                sumYY += dy * dy;
            }

            if (sumXX <= 0.0 || sumYY <= 0.0)
                return 0.0;

            return sumXY / Math.Sqrt(sumXX * sumYY);
        }
    }
}
