using InnovEnergy.S3.Drivers;
using InnovEnergy.S3.Metadata;
using InnovEnergy.S3.Records;
using InnovEnergy.S3.Records.Fields;
using InnovEnergy.S3.Records.Specialized;
using InnovEnergy.Time.Unix;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static InnovEnergy.Time.Unix.UnixTimeSpan;

namespace S3.Tests;

using Memory = Dictionary<String, TimeStampedRecord>;

[TestClass]
public class UnitTest1
{
    public IReadOnlyList<AggregationLevel> Levels { get; set; }

    [TestInitialize]
    public void Init()
    {
        Levels =  new []
        {
            new AggregationLevel( 2.Seconds(),  54.Weeks()),
            new AggregationLevel(10.Seconds(),  54.Weeks()),
            new AggregationLevel( 1.Minutes(),  54.Weeks()),
            new AggregationLevel( 5.Minutes(), 540.Weeks()),
            new AggregationLevel(15.Minutes(), 540.Weeks()),
            new AggregationLevel( 1.Hours()  , 540.Weeks()),
            new AggregationLevel( 6.Hours()  , 540.Weeks()),
            new AggregationLevel( 1.Days()   , 540.Weeks()),
            new AggregationLevel( 1.Weeks()  , Forever)
        };

    }

    [TestMethod]
    public void Test1()
    {
        var retentionPeriod = 4.Seconds();
        var samplePeriod    = 2.Seconds();

        var level0 = new AggregationLevel(samplePeriod, retentionPeriod);
        var levels = new[] {level0};

        var records = level0
            .RangeExclusive(UnixTime.Epoch, UnixTime.Epoch + 4 * level0.SamplePeriod)
            .Select(TimeStampedRecord.Empty);

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, UnixTime.Epoch, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        Assert.AreEqual(memory.Count, (Int32) (retentionPeriod/samplePeriod));
    }

    [TestMethod]
    public void Test2()
    {
        var l0 = new AggregationLevel(2.Seconds(), Forever);
        var l1 = new AggregationLevel(10.Seconds(), Forever);

        var levels = new[] {l0, l1};

        var l1Key = l1.SamplePeriod.ToString();
        var l0Key = l0.SamplePeriod.ToString();

        var records = l0
            .RangeExclusive(UnixTime.Epoch, UnixTime.Epoch + l1.SamplePeriod)
            .Select(TimeStampedRecord.Empty);

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, UnixTime.Epoch, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        var n0 = memory.Count(kv => kv.Key.StartsWith(l0Key));
        var n1 = memory.Count(kv => kv.Key.StartsWith(l1Key));

        Assert.AreEqual(5, n0);
        Assert.AreEqual(1, n1);
    }

    [TestMethod]
    public void Test3()
    {
        var l0 = new AggregationLevel(2.Seconds(), Forever);
        var l1 = new AggregationLevel(10.Seconds(), Forever);

        var levels = new[] {l0, l1};

        var l1Key = l1.SamplePeriod.ToString();
        var l0Key = l0.SamplePeriod.ToString();

        var records = l0
            .RangeExclusive(UnixTime.Epoch, UnixTime.Epoch + l1.SamplePeriod + l0.SamplePeriod)
            .Select(TimeStampedRecord.Empty);

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, UnixTime.Epoch, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        var n0 = memory.Count(kv => kv.Key.StartsWith(l0Key));
        var n1 = memory.Count(kv => kv.Key.StartsWith(l1Key));

        Assert.AreEqual(6, n0);
        Assert.AreEqual(2, n1);
    }

    [TestMethod]
    public void Test4()
    {
        var l0 = new AggregationLevel(2.Seconds(), Forever);
        var l1 = new AggregationLevel(10.Seconds(), Forever);

        var levels = new[] {l0, l1};

        var l1Key = l1.SamplePeriod.ToString();
        var l0Key = l0.SamplePeriod.ToString();

        var startTime = UnixTime.Epoch + l0.SamplePeriod;
        var endTime   = UnixTime.Epoch + l1.SamplePeriod + l0.SamplePeriod;

        var records = l0
            .RangeExclusive(startTime, endTime)
            .Select(TimeStampedRecord.Empty);

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, startTime, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        var n0 = memory.Count(kv => kv.Key.StartsWith(l0Key));
        var n1 = memory.Count(kv => kv.Key.StartsWith(l1Key));

        Assert.AreEqual(5, n0);
        Assert.AreEqual(2, n1);
    }


    [TestMethod]
    public void Test5()
    {
        var l0 = new AggregationLevel(2.Seconds(), Forever);
        var l1 = new AggregationLevel(10.Seconds(), Forever);

        var levels = new[] {l0, l1};

        var l1Key = l1.SamplePeriod.ToString();

        var startTime = UnixTime.Epoch;
        var endTime   = UnixTime.Epoch + l1.SamplePeriod;

        Record CreateRecord(Int32 n) => Record.ParseDict(new () {["Number"] = n});

        var records = l0
            .RangeExclusive(startTime, endTime)
            .Select((t, i) => CreateRecord(i).TimeStamped(t));

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, startTime, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        var f1 = memory
            .Single(kv => kv.Key.StartsWith(l1Key))
            .Value
            .Record
            .GetField<NumberField>("Number");

        Assert.AreEqual(Enumerable.Range(0, (Int32) (l1.SamplePeriod / l0.SamplePeriod)).Average(), f1.Value);
    }

    [TestMethod]
    public void Test6()
    {
        var l0 = new AggregationLevel(2.Seconds(), Forever);
        var l1 = new AggregationLevel(10.Seconds(), Forever);

        var levels = new[] {l0, l1};

        var l1Key = l1.SamplePeriod.ToString();

        var startTime = UnixTime.Epoch;
        var endTime   = UnixTime.Epoch + l1.SamplePeriod;



        Record CreateRecord(Int32 n)
        {
            var subRec = Record.ParseDict(new() {["Number"] = n});

            return Record.ParseDict(new (){["Sub"] = subRec});

            // return new Record(new Record(new NumberField("Number", n)));
        }

        var records = l0
            .RangeExclusive(startTime, endTime)
            .Select((t, i) => CreateRecord(i).TimeStamped(t));

        var memory = new Memory();

        using (var d = new MemoryDriver(levels, startTime, memory))
            foreach (var record in records)
                d.WriteRecord(record);

        var f1 = memory
            .Single(kv => kv.Key.StartsWith(l1Key))
            .Value
            .Record
            .GetField<Record>("Sub")
            .GetField<NumberField>("Number");

        Assert.AreEqual(Enumerable.Range(0, (Int32) (l1.SamplePeriod / l0.SamplePeriod)).Average(), f1.Value);
    }

}