using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Common.Models;
using NT8.Core.Indicators;

namespace NT8.Core.Tests.Indicators
{
    [TestClass]
    public class AVWAPCalculatorTests
    {
        [TestMethod]
        public void Constructor_InitializesWithAnchorMode()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            Assert.AreEqual(AVWAPAnchorMode.Day, calc.GetAnchorMode());
        }

        [TestMethod]
        public void Calculate_NullBars_ThrowsArgumentNullException()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                calc.Calculate(null, DateTime.UtcNow);
            });
        }

        [TestMethod]
        public void Calculate_NoEligibleBars_ReturnsZero()
        {
            var anchor = DateTime.UtcNow;
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, anchor);

            var bars = new List<BarData>();
            bars.Add(new BarData("ES", anchor.AddMinutes(-2), 100, 101, 99, 100, 1000, TimeSpan.FromMinutes(1)));

            var value = calc.Calculate(bars, anchor);

            Assert.AreEqual(0.0, value, 0.000001);
        }

        [TestMethod]
        public void Calculate_SingleBar_ReturnsTypicalPrice()
        {
            var anchor = DateTime.UtcNow;
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, anchor);

            var bars = new List<BarData>();
            bars.Add(new BarData("ES", anchor, 100, 103, 97, 101, 10, TimeSpan.FromMinutes(1)));

            var value = calc.Calculate(bars, anchor);

            var expected = (103.0 + 97.0 + 101.0) / 3.0;
            Assert.AreEqual(expected, value, 0.000001);
        }

        [TestMethod]
        public void Calculate_MultipleBars_ReturnsVolumeWeightedValue()
        {
            var anchor = DateTime.UtcNow;
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, anchor);

            var bars = new List<BarData>();
            bars.Add(new BarData("ES", anchor, 100, 102, 98, 101, 10, TimeSpan.FromMinutes(1))); // typical 100.3333
            bars.Add(new BarData("ES", anchor.AddMinutes(1), 101, 103, 99, 102, 30, TimeSpan.FromMinutes(1))); // typical 101.3333

            var value = calc.Calculate(bars, anchor);

            var p1 = (102.0 + 98.0 + 101.0) / 3.0;
            var p2 = (103.0 + 99.0 + 102.0) / 3.0;
            var expected = ((p1 * 10.0) + (p2 * 30.0)) / 40.0;
            Assert.AreEqual(expected, value, 0.000001);
        }

        [TestMethod]
        public void Update_NegativeVolume_ThrowsArgumentException()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            Assert.ThrowsException<ArgumentException>(delegate
            {
                calc.Update(100.0, -1);
            });
        }

        [TestMethod]
        public void Update_ThenGetCurrentValue_ReturnsWeightedAverage()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            calc.Update(100.0, 10);
            calc.Update(110.0, 30);

            var value = calc.GetCurrentValue();
            var expected = ((100.0 * 10.0) + (110.0 * 30.0)) / 40.0;

            Assert.AreEqual(expected, value, 0.000001);
        }

        [TestMethod]
        public void GetSlope_InsufficientHistory_ReturnsZero()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            calc.Update(100.0, 10);

            var slope = calc.GetSlope(5);
            Assert.AreEqual(0.0, slope, 0.000001);
        }

        [TestMethod]
        public void GetSlope_WithHistory_ReturnsPositiveForRisingSeries()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            calc.Update(100.0, 10);
            calc.Update(101.0, 10);
            calc.Update(102.0, 10);
            calc.Update(103.0, 10);
            calc.Update(104.0, 10);
            calc.Update(105.0, 10);

            var slope = calc.GetSlope(3);

            Assert.IsTrue(slope > 0.0);
        }

        [TestMethod]
        public void ResetAnchor_ClearsAccumulation()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            calc.Update(100.0, 10);
            Assert.IsTrue(calc.GetCurrentValue() > 0.0);

            calc.ResetAnchor(DateTime.UtcNow.AddHours(1));

            Assert.AreEqual(0.0, calc.GetCurrentValue(), 0.000001);
        }

        [TestMethod]
        public void SetAnchorMode_ChangesMode()
        {
            var calc = new AVWAPCalculator(AVWAPAnchorMode.Day, DateTime.UtcNow);
            calc.SetAnchorMode(AVWAPAnchorMode.Week);
            Assert.AreEqual(AVWAPAnchorMode.Week, calc.GetAnchorMode());
        }
    }
}
