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

namespace NT8.Core.Indicators
{
    /// <summary>
    /// Anchor mode for AVWAP reset behavior.
    /// </summary>
    public enum AVWAPAnchorMode
    {
        /// <summary>
        /// Reset at session/day start.
        /// </summary>
        Day = 0,

        /// <summary>
        /// Reset at week start.
        /// </summary>
        Week = 1,

        /// <summary>
        /// Reset at custom provided anchor time.
        /// </summary>
        Custom = 2
    }

    /// <summary>
    /// Anchored VWAP calculator with rolling updates and slope estimation.
    /// Thread-safe for live multi-caller usage.
    /// </summary>
    public class AVWAPCalculator
    {
        private readonly object _lock = new object();
        private readonly List<double> _vwapHistory;

        private DateTime _anchorTime;
        private double _sumPriceVolume;
        private double _sumVolume;
        private AVWAPAnchorMode _anchorMode;

        /// <summary>
        /// Creates a new AVWAP calculator.
        /// </summary>
        /// <param name="anchorMode">Anchor mode.</param>
        /// <param name="anchorTime">Initial anchor time.</param>
        public AVWAPCalculator(AVWAPAnchorMode anchorMode, DateTime anchorTime)
        {
            _anchorMode = anchorMode;
            _anchorTime = anchorTime;
            _sumPriceVolume = 0.0;
            _sumVolume = 0.0;
            _vwapHistory = new List<double>();
        }

        /// <summary>
        /// Calculates anchored VWAP from bars starting at anchor time.
        /// </summary>
        /// <param name="bars">Source bars in chronological order.</param>
        /// <param name="anchorTime">Anchor start time.</param>
        /// <returns>Calculated AVWAP value or 0.0 if no eligible bars.</returns>
        public double Calculate(List<BarData> bars, DateTime anchorTime)
        {
            if (bars == null)
                throw new ArgumentNullException("bars");

            lock (_lock)
            {
                _anchorTime = anchorTime;
                _sumPriceVolume = 0.0;
                _sumVolume = 0.0;
                _vwapHistory.Clear();

                for (var i = 0; i < bars.Count; i++)
                {
                    var bar = bars[i];
                    if (bar == null)
                        continue;

                    if (bar.Time < anchorTime)
                        continue;

                    var price = GetTypicalPrice(bar);
                    var volume = Math.Max(0L, bar.Volume);

                    _sumPriceVolume += price * volume;
                    _sumVolume += volume;

                    var vwap = _sumVolume > 0.0 ? _sumPriceVolume / _sumVolume : 0.0;
                    _vwapHistory.Add(vwap);
                }

                if (_sumVolume <= 0.0)
                    return 0.0;

                return _sumPriceVolume / _sumVolume;
            }
        }

        /// <summary>
        /// Updates AVWAP state with one new trade/bar observation.
        /// </summary>
        /// <param name="price">Current price.</param>
        /// <param name="volume">Current volume.</param>
        public void Update(double price, long volume)
        {
            if (volume < 0)
                throw new ArgumentException("volume must be non-negative", "volume");

            lock (_lock)
            {
                _sumPriceVolume += price * volume;
                _sumVolume += volume;

                var vwap = _sumVolume > 0.0 ? _sumPriceVolume / _sumVolume : 0.0;
                _vwapHistory.Add(vwap);

                if (_vwapHistory.Count > 2000)
                    _vwapHistory.RemoveAt(0);
            }
        }

        /// <summary>
        /// Returns AVWAP slope over lookback bars.
        /// </summary>
        /// <param name="lookback">Lookback bars.</param>
        /// <returns>Slope per bar.</returns>
        public double GetSlope(int lookback)
        {
            if (lookback <= 0)
                throw new ArgumentException("lookback must be greater than zero", "lookback");

            lock (_lock)
            {
                if (_vwapHistory.Count <= lookback)
                    return 0.0;

                var lastIndex = _vwapHistory.Count - 1;
                var current = _vwapHistory[lastIndex];
                var prior = _vwapHistory[lastIndex - lookback];
                return (current - prior) / lookback;
            }
        }

        /// <summary>
        /// Resets AVWAP accumulation to a new anchor.
        /// </summary>
        /// <param name="newAnchor">New anchor time.</param>
        public void ResetAnchor(DateTime newAnchor)
        {
            lock (_lock)
            {
                _anchorTime = newAnchor;
                _sumPriceVolume = 0.0;
                _sumVolume = 0.0;
                _vwapHistory.Clear();
            }
        }

        /// <summary>
        /// Gets current AVWAP from rolling state.
        /// </summary>
        /// <returns>Current AVWAP.</returns>
        public double GetCurrentValue()
        {
            lock (_lock)
            {
                return _sumVolume > 0.0 ? _sumPriceVolume / _sumVolume : 0.0;
            }
        }

        /// <summary>
        /// Gets current anchor mode.
        /// </summary>
        /// <returns>Anchor mode.</returns>
        public AVWAPAnchorMode GetAnchorMode()
        {
            lock (_lock)
            {
                return _anchorMode;
            }
        }

        /// <summary>
        /// Sets anchor mode.
        /// </summary>
        /// <param name="mode">Anchor mode.</param>
        public void SetAnchorMode(AVWAPAnchorMode mode)
        {
            lock (_lock)
            {
                _anchorMode = mode;
            }
        }

        private static double GetTypicalPrice(BarData bar)
        {
            return (bar.High + bar.Low + bar.Close) / 3.0;
        }
    }
}
