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

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Calculates one confluence factor from strategy and market context.
    /// </summary>
    public interface IFactorCalculator
    {
        /// <summary>
        /// Factor type produced by this calculator.
        /// </summary>
        FactorType Type { get; }

        /// <summary>
        /// Calculates the confluence factor.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Calculated confluence factor.</returns>
        ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar);
    }

    /// <summary>
    /// ORB setup quality calculator.
    /// </summary>
    public class OrbSetupFactorCalculator : IFactorCalculator
    {
        /// <summary>
        /// Gets the factor type.
        /// </summary>
        public FactorType Type
        {
            get { return FactorType.Setup; }
        }

        /// <summary>
        /// Calculates ORB setup validity score.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Setup confluence factor.</returns>
        public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
        {
            if (intent == null)
                throw new ArgumentNullException("intent");
            if (context == null)
                throw new ArgumentNullException("context");
            if (bar == null)
                throw new ArgumentNullException("bar");

            var score = 0.0;
            var details = new Dictionary<string, object>();

            var orbRange = bar.High - bar.Low;
            details.Add("orb_range", orbRange);

            if (orbRange >= 1.0)
                score += 0.3;

            var body = System.Math.Abs(bar.Close - bar.Open);
            details.Add("bar_body", body);
            if (body >= (orbRange * 0.5))
                score += 0.2;

            var averageVolume = GetDouble(context.CustomData, "avg_volume", 1.0);
            details.Add("avg_volume", averageVolume);
            details.Add("current_volume", bar.Volume);
            if (averageVolume > 0.0 && bar.Volume > (long)(averageVolume * 1.1))
                score += 0.2;

            var minutesFromSessionOpen = (bar.Time - context.Session.SessionStart).TotalMinutes;
            details.Add("minutes_from_open", minutesFromSessionOpen);
            if (minutesFromSessionOpen >= 0 && minutesFromSessionOpen <= 120)
                score += 0.3;

            if (score > 1.0)
                score = 1.0;

            return new ConfluenceFactor(
                FactorType.Setup,
                "ORB Setup Validity",
                score,
                1.0,
                "ORB validity based on range, candle quality, volume, and timing",
                details);
        }

        private static double GetDouble(Dictionary<string, object> data, string key, double defaultValue)
        {
            if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null)
                return defaultValue;

            var value = data[key];
            if (value is double)
                return (double)value;
            if (value is float)
                return (double)(float)value;
            if (value is int)
                return (double)(int)value;
            if (value is long)
                return (double)(long)value;

            return defaultValue;
        }
    }

    /// <summary>
    /// Trend alignment calculator.
    /// </summary>
    public class TrendAlignmentFactorCalculator : IFactorCalculator
    {
        /// <summary>
        /// Gets the factor type.
        /// </summary>
        public FactorType Type
        {
            get { return FactorType.Trend; }
        }

        /// <summary>
        /// Calculates trend alignment score.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Trend confluence factor.</returns>
        public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
        {
            if (intent == null)
                throw new ArgumentNullException("intent");
            if (context == null)
                throw new ArgumentNullException("context");
            if (bar == null)
                throw new ArgumentNullException("bar");

            var details = new Dictionary<string, object>();
            var score = 0.0;

            var avwap = GetDouble(context.CustomData, "avwap", bar.Close);
            var avwapSlope = GetDouble(context.CustomData, "avwap_slope", 0.0);
            var trendConfirm = GetDouble(context.CustomData, "trend_confirm", 0.0);

            details.Add("avwap", avwap);
            details.Add("avwap_slope", avwapSlope);
            details.Add("trend_confirm", trendConfirm);

            var isLong = intent.Side == OrderSide.Buy;
            var priceAligned = isLong ? bar.Close > avwap : bar.Close < avwap;
            if (priceAligned)
                score += 0.4;

            var slopeAligned = isLong ? avwapSlope > 0.0 : avwapSlope < 0.0;
            if (slopeAligned)
                score += 0.3;

            if (trendConfirm > 0.5)
                score += 0.3;

            if (score > 1.0)
                score = 1.0;

            return new ConfluenceFactor(
                FactorType.Trend,
                "Trend Alignment",
                score,
                1.0,
                "Trend alignment using AVWAP location, slope, and confirmation",
                details);
        }

        private static double GetDouble(Dictionary<string, object> data, string key, double defaultValue)
        {
            if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null)
                return defaultValue;

            var value = data[key];
            if (value is double)
                return (double)value;
            if (value is float)
                return (double)(float)value;
            if (value is int)
                return (double)(int)value;
            if (value is long)
                return (double)(long)value;

            return defaultValue;
        }
    }

    /// <summary>
    /// Volatility regime suitability calculator.
    /// </summary>
    public class VolatilityRegimeFactorCalculator : IFactorCalculator
    {
        /// <summary>
        /// Gets the factor type.
        /// </summary>
        public FactorType Type
        {
            get { return FactorType.Volatility; }
        }

        /// <summary>
        /// Calculates volatility regime score.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Volatility confluence factor.</returns>
        public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
        {
            if (intent == null)
                throw new ArgumentNullException("intent");
            if (context == null)
                throw new ArgumentNullException("context");
            if (bar == null)
                throw new ArgumentNullException("bar");

            var details = new Dictionary<string, object>();
            var currentAtr = GetDouble(context.CustomData, "current_atr", 1.0);
            var normalAtr = GetDouble(context.CustomData, "normal_atr", 1.0);

            details.Add("current_atr", currentAtr);
            details.Add("normal_atr", normalAtr);

            var ratio = normalAtr > 0.0 ? currentAtr / normalAtr : 1.0;
            details.Add("atr_ratio", ratio);

            var score = 0.3;
            if (ratio >= 0.8 && ratio <= 1.2)
                score = 1.0;
            else if (ratio < 0.8)
                score = 0.7;
            else if (ratio > 1.2 && ratio <= 1.5)
                score = 0.5;
            else if (ratio > 1.5)
                score = 0.3;

            return new ConfluenceFactor(
                FactorType.Volatility,
                "Volatility Regime",
                score,
                1.0,
                "Volatility suitability from ATR ratio",
                details);
        }

        private static double GetDouble(Dictionary<string, object> data, string key, double defaultValue)
        {
            if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null)
                return defaultValue;

            var value = data[key];
            if (value is double)
                return (double)value;
            if (value is float)
                return (double)(float)value;
            if (value is int)
                return (double)(int)value;
            if (value is long)
                return (double)(long)value;

            return defaultValue;
        }
    }

    /// <summary>
    /// Session timing suitability calculator.
    /// </summary>
    public class TimeInSessionFactorCalculator : IFactorCalculator
    {
        /// <summary>
        /// Gets the factor type.
        /// </summary>
        public FactorType Type
        {
            get { return FactorType.Timing; }
        }

        /// <summary>
        /// Calculates session timing score.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Timing confluence factor.</returns>
        public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
        {
            if (intent == null)
                throw new ArgumentNullException("intent");
            if (context == null)
                throw new ArgumentNullException("context");
            if (bar == null)
                throw new ArgumentNullException("bar");

            var details = new Dictionary<string, object>();
            var t = bar.Time.TimeOfDay;
            details.Add("time_of_day", t);

            var score = 0.3;
            var open = new TimeSpan(9, 30, 0);
            var firstTwoHoursEnd = new TimeSpan(11, 30, 0);
            var middayEnd = new TimeSpan(14, 0, 0);
            var lastHourStart = new TimeSpan(15, 0, 0);
            var close = new TimeSpan(16, 0, 0);

            if (t >= open && t < firstTwoHoursEnd)
                score = 1.0;
            else if (t >= firstTwoHoursEnd && t < middayEnd)
                score = 0.6;
            else if (t >= lastHourStart && t < close)
                score = 0.8;
            else
                score = 0.3;

            return new ConfluenceFactor(
                FactorType.Timing,
                "Time In Session",
                score,
                1.0,
                "Session timing suitability",
                details);
        }
    }

    /// <summary>
    /// Recent execution quality calculator.
    /// </summary>
    public class ExecutionQualityFactorCalculator : IFactorCalculator
    {
        /// <summary>
        /// Gets the factor type.
        /// </summary>
        public FactorType Type
        {
            get { return FactorType.ExecutionQuality; }
        }

        /// <summary>
        /// Calculates execution quality score from recent fills.
        /// </summary>
        /// <param name="intent">Current strategy intent.</param>
        /// <param name="context">Current strategy context.</param>
        /// <param name="bar">Current bar data.</param>
        /// <returns>Execution quality factor.</returns>
        public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
        {
            if (intent == null)
                throw new ArgumentNullException("intent");
            if (context == null)
                throw new ArgumentNullException("context");
            if (bar == null)
                throw new ArgumentNullException("bar");

            var details = new Dictionary<string, object>();
            var quality = GetDouble(context.CustomData, "recent_execution_quality", 0.6);
            details.Add("recent_execution_quality", quality);

            var score = 0.6;
            if (quality >= 0.9)
                score = 1.0;
            else if (quality >= 0.75)
                score = 0.8;
            else if (quality >= 0.6)
                score = 0.6;
            else
                score = 0.4;

            return new ConfluenceFactor(
                FactorType.ExecutionQuality,
                "Recent Execution Quality",
                score,
                1.0,
                "Recent execution quality suitability",
                details);
        }

        private static double GetDouble(Dictionary<string, object> data, string key, double defaultValue)
        {
            if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null)
                return defaultValue;

            var value = data[key];
            if (value is double)
                return (double)value;
            if (value is float)
                return (double)(float)value;
            if (value is int)
                return (double)(int)value;
            if (value is long)
                return (double)(long)value;

            return defaultValue;
        }
    }
}
