using System;
using System.Collections.Generic;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Indicators;
using NT8.Core.Intelligence;
using NT8.Core.Logging;

namespace NT8.Strategies.Examples
{
    /// <summary>
    /// Opening Range Breakout strategy with Phase 4 confluence and grade-aware intent metadata.
    /// </summary>
    public class SimpleORBStrategy : IStrategy
    {
        private readonly object _lock = new object();

        private readonly int _openingRangeMinutes;
        private readonly double _stdDevMultiplier;

        private ILogger _logger;
        private StrategyConfig _config;
        private ConfluenceScorer _scorer;
        private GradeFilter _gradeFilter;
        private RiskModeManager _riskModeManager;
        private AVWAPCalculator _avwapCalculator;
        private VolumeProfileAnalyzer _volumeProfileAnalyzer;
        private List<IFactorCalculator> _factorCalculators;

        private DateTime _currentSessionDate;
        private DateTime _openingRangeStart;
        private DateTime _openingRangeEnd;
        private double _openingRangeHigh;
        private double _openingRangeLow;
        private bool _openingRangeReady;
        private bool _tradeTaken;
        private int _consecutiveWins;
        private int _consecutiveLosses;

        /// <summary>
        /// Gets strategy metadata.
        /// </summary>
        public StrategyMetadata Metadata { get; private set; }

        /// <summary>
        /// Creates a new strategy with default ORB configuration.
        /// </summary>
        public SimpleORBStrategy()
            : this(30, 1.0)
        {
        }

        /// <summary>
        /// Creates a new strategy with custom ORB configuration.
        /// </summary>
        /// <param name="openingRangeMinutes">Opening range period in minutes.</param>
        /// <param name="stdDevMultiplier">Breakout volatility multiplier.</param>
        public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier)
        {
            if (openingRangeMinutes <= 0)
                throw new ArgumentException("openingRangeMinutes must be greater than zero", "openingRangeMinutes");

            if (stdDevMultiplier <= 0.0)
                throw new ArgumentException("stdDevMultiplier must be greater than zero", "stdDevMultiplier");

            _openingRangeMinutes = openingRangeMinutes;
            _stdDevMultiplier = stdDevMultiplier;

            _currentSessionDate = DateTime.MinValue;
            _openingRangeStart = DateTime.MinValue;
            _openingRangeEnd = DateTime.MinValue;
            _openingRangeHigh = Double.MinValue;
            _openingRangeLow = Double.MaxValue;
            _openingRangeReady = false;
            _tradeTaken = false;
            _consecutiveWins = 0;
            _consecutiveLosses = 0;

            Metadata = new StrategyMetadata(
                "Simple ORB",
                "Opening Range Breakout strategy with confluence scoring",
                "2.0",
                "NT8 SDK Team",
                new string[] { "ES", "NQ", "YM" },
                20);
        }

        /// <summary>
        /// Initializes strategy dependencies.
        /// </summary>
        /// <param name="config">Strategy configuration.</param>
        /// <param name="dataProvider">Market data provider.</param>
        /// <param name="logger">Logger instance.</param>
        public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            try
            {
                _logger = logger;
                _config = config;
                _scorer = new ConfluenceScorer(_logger, 500);
                _gradeFilter = new GradeFilter();
                _riskModeManager = new RiskModeManager(_logger);
                _avwapCalculator = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
                _volumeProfileAnalyzer = new VolumeProfileAnalyzer();

                _factorCalculators = new List<IFactorCalculator>();
                _factorCalculators.Add(new OrbSetupFactorCalculator());
                _factorCalculators.Add(new TrendAlignmentFactorCalculator());
                _factorCalculators.Add(new VolatilityRegimeFactorCalculator());
                _factorCalculators.Add(new TimeInSessionFactorCalculator());
                _factorCalculators.Add(new ExecutionQualityFactorCalculator());

                _logger.LogInformation(
                    "SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}",
                    _openingRangeMinutes,
                    _stdDevMultiplier);
            }
            catch (Exception ex)
            {
                if (_logger != null)
                    _logger.LogError("SimpleORBStrategy Initialize failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Processes bar data and returns a trade intent when breakout and confluence criteria pass.
        /// </summary>
        /// <param name="bar">Current bar.</param>
        /// <param name="context">Strategy context.</param>
        /// <returns>Trade intent when signal is accepted; otherwise null.</returns>
        public StrategyIntent OnBar(BarData bar, StrategyContext context)
        {
            if (bar == null)
                throw new ArgumentNullException("bar");
            if (context == null)
                throw new ArgumentNullException("context");

            try
            {
                lock (_lock)
                {
                    EnsureInitialized();

                    UpdateRiskMode(context);
                    UpdateConfluenceInputs(bar, context);

                    if (_currentSessionDate != context.CurrentTime.Date)
                    {
                        ResetSession(context.Session != null ? context.Session.SessionStart : context.CurrentTime.Date);
                    }

                    // Only trade during RTH
                    if (context.Session == null || !context.Session.IsRth)
                        return null;

                    if (bar.Time <= _openingRangeEnd)
                    {
                        UpdateOpeningRange(bar);
                        return null;
                    }

                    if (!_openingRangeReady)
                    {
                        if (_openingRangeHigh > _openingRangeLow)
                            _openingRangeReady = true;
                        else
                            return null;
                    }

                    if (_tradeTaken)
                        return null;

                    var openingRange = _openingRangeHigh - _openingRangeLow;
                    var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0);
                    if (volatilityBuffer < 0.0)
                        volatilityBuffer = 0.0;

                    var longTrigger = _openingRangeHigh + volatilityBuffer;
                    var shortTrigger = _openingRangeLow - volatilityBuffer;

                    StrategyIntent candidate = null;
                    if (bar.Close > longTrigger)
                        candidate = CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close);
                    else if (bar.Close < shortTrigger)
                        candidate = CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close);

                    if (candidate == null)
                        return null;

                    var score = _scorer.CalculateScore(candidate, context, bar, _factorCalculators);
                    var mode = _riskModeManager.GetCurrentMode();

                    if (!_gradeFilter.ShouldAcceptTrade(score.Grade, mode))
                    {
                        var reason = _gradeFilter.GetRejectionReason(score.Grade, mode);
                        _logger.LogInformation(
                            "SimpleORBStrategy rejected intent for {0}: Grade={1}, Mode={2}, Reason={3}",
                            candidate.Symbol,
                            score.Grade,
                            mode,
                            reason);
                        return null;
                    }

                    var gradeMultiplier = _gradeFilter.GetSizeMultiplier(score.Grade, mode);
                    var modeConfig = _riskModeManager.GetModeConfig(mode);
                    var combinedMultiplier = gradeMultiplier * modeConfig.SizeMultiplier;

                    candidate.Confidence = score.WeightedScore;
                    candidate.Reason = string.Format("{0}; grade={1}; mode={2}", candidate.Reason, score.Grade, mode);
                    candidate.Metadata["confluence_score"] = score.WeightedScore;
                    candidate.Metadata["trade_grade"] = score.Grade.ToString();
                    candidate.Metadata["risk_mode"] = mode.ToString();
                    candidate.Metadata["grade_multiplier"] = gradeMultiplier;
                    candidate.Metadata["mode_multiplier"] = modeConfig.SizeMultiplier;
                    candidate.Metadata["combined_multiplier"] = combinedMultiplier;

                    _tradeTaken = true;

                    _logger.LogInformation(
                        "SimpleORBStrategy accepted intent for {0}: Side={1}, Grade={2}, Mode={3}, Score={4:F3}, Mult={5:F2}",
                        candidate.Symbol,
                        candidate.Side,
                        score.Grade,
                        mode,
                        score.WeightedScore,
                        combinedMultiplier);

                    return candidate;
                }
            }
            catch (Exception ex)
            {
                if (_logger != null)
                    _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Processes tick data. This strategy does not use tick-level logic.
        /// </summary>
        /// <param name="tick">Tick data.</param>
        /// <param name="context">Strategy context.</param>
        /// <returns>Always null for this strategy.</returns>
        public StrategyIntent OnTick(TickData tick, StrategyContext context)
        {
            return null;
        }

        /// <summary>
        /// Returns current strategy parameters.
        /// </summary>
        /// <returns>Parameter map.</returns>
        public Dictionary<string, object> GetParameters()
        {
            lock (_lock)
            {
                var parameters = new Dictionary<string, object>();
                parameters.Add("opening_range_minutes", _openingRangeMinutes);
                parameters.Add("std_dev_multiplier", _stdDevMultiplier);
                return parameters;
            }
        }

        /// <summary>
        /// Updates strategy parameters.
        /// </summary>
        /// <param name="parameters">Parameter map.</param>
        public void SetParameters(Dictionary<string, object> parameters)
        {
            // Constructor-bound parameters intentionally remain immutable for deterministic behavior.
        }

        private void EnsureInitialized()
        {
            if (_logger == null)
                throw new InvalidOperationException("Strategy must be initialized before OnBar processing");
            if (_scorer == null || _gradeFilter == null || _riskModeManager == null)
                throw new InvalidOperationException("Intelligence components are not initialized");
        }

        private void UpdateRiskMode(StrategyContext context)
        {
            var dailyPnl = 0.0;
            if (context.Account != null)
                dailyPnl = context.Account.DailyPnL;

            _riskModeManager.UpdateRiskMode(dailyPnl, _consecutiveWins, _consecutiveLosses);
        }

        private void UpdateConfluenceInputs(BarData bar, StrategyContext context)
        {
            _avwapCalculator.Update(bar.Close, bar.Volume);
            var avwap = _avwapCalculator.GetCurrentValue();
            var avwapSlope = _avwapCalculator.GetSlope(10);

            var bars = new List<BarData>();
            bars.Add(bar);
            var valueArea = _volumeProfileAnalyzer.CalculateValueArea(bars);

            if (context.CustomData == null)
                context.CustomData = new Dictionary<string, object>();

            context.CustomData["current_bar"] = bar;
            context.CustomData["avwap"] = avwap;
            context.CustomData["avwap_slope"] = avwapSlope;
            context.CustomData["trend_confirm"] = avwapSlope > 0.0 ? 1.0 : 0.0;
            context.CustomData["current_atr"] = Math.Max(0.01, bar.High - bar.Low);
            context.CustomData["normal_atr"] = Math.Max(0.01, valueArea.ValueAreaHigh - valueArea.ValueAreaLow);
            context.CustomData["recent_execution_quality"] = 0.8;
            context.CustomData["avg_volume"] = (double)bar.Volume;
        }

        private void ResetSession(DateTime sessionStart)
        {
            _currentSessionDate = sessionStart.Date;
            _openingRangeStart = sessionStart;
            _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes);
            _openingRangeHigh = Double.MinValue;
            _openingRangeLow = Double.MaxValue;
            _openingRangeReady = false;
            _tradeTaken = false;
        }

        private void UpdateOpeningRange(BarData bar)
        {
            if (bar.High > _openingRangeHigh)
                _openingRangeHigh = bar.High;

            if (bar.Low < _openingRangeLow)
                _openingRangeLow = bar.Low;
        }

        private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice)
        {
            var stopTicks = _config != null && _config.Parameters.ContainsKey("StopTicks")
                ? (int)_config.Parameters["StopTicks"]
                : 8;
            var targetTicks = _config != null && _config.Parameters.ContainsKey("TargetTicks")
                ? (int)_config.Parameters["TargetTicks"]
                : 16;

            var metadata = new Dictionary<string, object>();
            metadata.Add("orb_high", _openingRangeHigh);
            metadata.Add("orb_low", _openingRangeLow);
            metadata.Add("orb_range", openingRange);
            metadata.Add("trigger_price", lastPrice);
            metadata.Add("multiplier", _stdDevMultiplier);
            metadata.Add("opening_range_start", _openingRangeStart);
            metadata.Add("opening_range_end", _openingRangeEnd);

            return new StrategyIntent(
                symbol,
                side,
                OrderType.Market,
                null,
                stopTicks,
                targetTicks,
                0.75,
                "ORB breakout signal",
                metadata);
        }
    }
}
