using System;
using System.Collections.Generic;
using NT8.Core.Common.Models;

namespace NT8.Core.MarketData
{
    /// <summary>
    /// Validates market data quality and detects anomalies
    /// </summary>
    public class DataQualityValidator
    {
        /// <summary>
        /// Configuration for data quality validation
        /// </summary>
        public class DataQualityConfig
        {
            /// <summary>
            /// Maximum allowed price change percentage between consecutive bars
            /// </summary>
            public double MaxPriceChangePercent { get; set; }

            /// <summary>
            /// Minimum allowed volume for a bar to be considered valid
            /// </summary>
            public long MinVolume { get; set; }

            /// <summary>
            /// Maximum allowed volume spike multiplier compared to average
            /// </summary>
            public double MaxVolumeSpikeMultiplier { get; set; }

            /// <summary>
            /// Constructor for DataQualityConfig
            /// </summary>
            public DataQualityConfig()
            {
                MaxPriceChangePercent = 10.0; // 10% default
                MinVolume = 1; // Minimum 1 volume
                MaxVolumeSpikeMultiplier = 100.0; // 100x volume spike threshold
            }
        }

        private readonly DataQualityConfig _config;
        private readonly Dictionary<string, List<double>> _priceHistory;
        private readonly Dictionary<string, List<long>> _volumeHistory;

        /// <summary>
        /// Constructor for DataQualityValidator
        /// </summary>
        public DataQualityValidator(DataQualityConfig config = null)
        {
            _config = config ?? new DataQualityConfig();
            _priceHistory = new Dictionary<string, List<double>>();
            _volumeHistory = new Dictionary<string, List<long>>();
        }

        /// <summary>
        /// Validate bar data quality and detect anomalies
        /// </summary>
        public DataQualityResult ValidateBar(BarData currentBar, BarData previousBar = null)
        {
            if (currentBar == null)
                throw new ArgumentNullException("currentBar");

            var result = new DataQualityResult();

            // Basic validation
            if (!BasicBarValidation(currentBar))
            {
                result.IsValid = false;
                result.Reasons.Add("Basic validation failed");
                return result;
            }

            // Previous bar validation
            if (previousBar != null)
            {
                // Price change validation
                var priceChangePercent = CalculatePriceChangePercent(previousBar.Close, currentBar.Open);
                if (Math.Abs(priceChangePercent) > _config.MaxPriceChangePercent)
                {
                    result.IsValid = false;
                    result.Reasons.Add(string.Format("Price change {0:F2}% exceeds threshold of {1:F2}%",
                        priceChangePercent, _config.MaxPriceChangePercent));
                }

                // Volume validation
                if (currentBar.Volume < _config.MinVolume)
                {
                    result.IsValid = false;
                    result.Reasons.Add(string.Format("Volume {0} below minimum threshold of {1}",
                        currentBar.Volume, _config.MinVolume));
                }

                // Volume spike detection
                var avgVolume = CalculateAverageVolume(previousBar.Symbol);
                if (avgVolume > 0 && currentBar.Volume > avgVolume * _config.MaxVolumeSpikeMultiplier)
                {
                    result.IsValid = false;
                    result.Reasons.Add(string.Format("Volume spike detected: {0} vs average {1:F0}",
                        currentBar.Volume, avgVolume));
                }
            }

            // Update history
            UpdateHistory(currentBar);

            result.IsValid = result.Reasons.Count == 0;
            return result;
        }

        /// <summary>
        /// Validate tick data quality and detect anomalies
        /// </summary>
        public DataQualityResult ValidateTick(TickData currentTick, TickData previousTick = null)
        {
            if (currentTick == null)
                throw new ArgumentNullException("currentTick");

            var result = new DataQualityResult();

            // Basic validation
            if (!BasicTickValidation(currentTick))
            {
                result.IsValid = false;
                result.Reasons.Add("Basic validation failed");
                return result;
            }

            // Previous tick validation
            if (previousTick != null)
            {
                // Price change validation
                var priceChangePercent = CalculatePriceChangePercent(previousTick.Price, currentTick.Price);
                if (Math.Abs(priceChangePercent) > _config.MaxPriceChangePercent)
                {
                    result.IsValid = false;
                    result.Reasons.Add(string.Format("Price change {0:F2}% exceeds threshold of {1:F2}%",
                        priceChangePercent, _config.MaxPriceChangePercent));
                }
            }

            result.IsValid = result.Reasons.Count == 0;
            return result;
        }

        /// <summary>
        /// Basic bar validation
        /// </summary>
        private bool BasicBarValidation(BarData bar)
        {
            // Check for reasonable price values
            if (bar.Open <= 0 || bar.High <= 0 || bar.Low <= 0 || bar.Close <= 0)
                return false;

            // Check for valid high/low relationships
            if (bar.High < bar.Low)
                return false;

            // Check if close price is within high/low range
            if (bar.Close < bar.Low || bar.Close > bar.High)
                return false;

            // Check for reasonable volume
            if (bar.Volume < 0)
                return false;

            return true;
        }

        /// <summary>
        /// Basic tick validation
        /// </summary>
        private bool BasicTickValidation(TickData tick)
        {
            // Check for reasonable price values
            if (tick.Price <= 0)
                return false;

            // Check for reasonable size
            if (tick.Size < 0)
                return false;

            return true;
        }

        /// <summary>
        /// Calculate price change percentage
        /// </summary>
        private double CalculatePriceChangePercent(double previousPrice, double currentPrice)
        {
            if (previousPrice == 0)
                return 0;

            return ((currentPrice - previousPrice) / previousPrice) * 100;
        }

        /// <summary>
        /// Calculate average volume for a symbol
        /// </summary>
        private double CalculateAverageVolume(string symbol)
        {
            List<long> volumes;
            if (!_volumeHistory.TryGetValue(symbol, out volumes) || volumes.Count == 0)
                return 0;

            long sum = 0;
            foreach (var volume in volumes)
            {
                sum += volume;
            }

            return (double)sum / volumes.Count;
        }

        /// <summary>
        /// Update price and volume history
        /// </summary>
        private void UpdateHistory(BarData bar)
        {
            // Update price history
            List<double> prices;
            if (!_priceHistory.TryGetValue(bar.Symbol, out prices))
            {
                prices = new List<double>();
                _priceHistory[bar.Symbol] = prices;
            }

            prices.Add(bar.Close);
            if (prices.Count > 100) // Keep only last 100 prices
            {
                prices.RemoveAt(0);
            }

            // Update volume history
            List<long> volumes;
            if (!_volumeHistory.TryGetValue(bar.Symbol, out volumes))
            {
                volumes = new List<long>();
                _volumeHistory[bar.Symbol] = volumes;
            }

            volumes.Add(bar.Volume);
            if (volumes.Count > 100) // Keep only last 100 volumes
            {
                volumes.RemoveAt(0);
            }
        }
    }

    /// <summary>
    /// Result of data quality validation
    /// </summary>
    public class DataQualityResult
    {
        /// <summary>
        /// Whether the data is valid
        /// </summary>
        public bool IsValid { get; set; }

        /// <summary>
        /// Reasons for validation failure
        /// </summary>
        public List<string> Reasons { get; set; }

        /// <summary>
        /// Constructor for DataQualityResult
        /// </summary>
        public DataQualityResult()
        {
            IsValid = true;
            Reasons = new List<string>();
        }
    }
}
