using Xunit;
using Tulip;
using System.Diagnostics.CodeAnalysis;

namespace QuanTAlib;

[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class TulipTests
{
    private readonly TBarSeries bars;
    private readonly GbmFeed feed;
    private readonly Random rnd;
    private readonly double range;
    private readonly int iterations;
    private readonly double[] data;
    private readonly double[] outdata;
    private readonly int skip;

    public TulipTests()
    {
        rnd = new((int)DateTime.Now.Ticks);
        feed = new(sigma: 0.5, mu: 0.0);
        bars = new(feed);
        range = 1e-9;
        feed.Add(10000);
        iterations = 3;
        skip = 500;
        data = feed.Close.v.ToArray();
        outdata = new double[data.Count()];
    }

    [Fact]
    public void SMA()
    {
        for (int run = 0; run < iterations; run++)
        {
            int period = rnd.Next(50) + 5;
            Sma ma = new(period);
            TSeries QL = new();
            foreach (TBar item in feed)
            { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }

            double[][] arrin = [data];
            double[][] arrout = [outdata];
            Tulip.Indicators.sma.Run(inputs: arrin, options: [period], outputs: arrout);
            Assert.Equal(QL.Length, arrout[0].Length);
            for (int i = QL.Length - 1; i > skip; i--)
            {
                double QL_item = QL[i].Value;
                double TU = i < period - 1 ? double.NaN : arrout[0][i - period + 1];
                Assert.InRange(TU - QL_item, -range, range);
            }
        }
    }

    [Fact]
    public void EMA()
    {
        for (int run = 0; run < iterations; run++)
        {
            int period = rnd.Next(30) + 5;
            Ema ma = new(period, useSma: false);
            TSeries QL = new();
            foreach (TBar item in feed)
            { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }

            double[][] arrin = [data];
            double[][] arrout = [outdata];
            Tulip.Indicators.ema.Run(inputs: arrin, options: [period], outputs: arrout);

            Assert.Equal(QL.Length, arrout[0].Length);
            for (int i = QL.Length - 1; i > skip * 2; i--)  //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip
            {
                double QL_item = QL[i].Value;
                double TU = arrout[0][i];
                Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU - QL_item}");
            }
        }
    }
}