using System;
using System.Globalization;

using Xunit;
using Xunit.Abstractions;

using CoinbaseEngine;
using Newtonsoft.Json;

namespace CoinbaseXUnitTest
{
    public class TestCommon : IDisposable
    {
        private readonly ITestOutputHelper _testOut;
        private readonly string protocol = "2,4,5105,23,1,32,42,2,3,77,15,700,16,17,711,18,22,649,29,712,78,25,23,689,584,624,629,745,87";

        public TestCommon(ITestOutputHelper testOutputHelper)
        {
            _testOut = testOutputHelper;
        }

        public void Dispose()
        { }

        [Theory]
        [InlineData("2020-01-05", 1578153600)]
        [InlineData("2020-05-11", 1589126400)]
        [InlineData("2020-09-04", 1599148800)]
        [InlineData("2023-11-05", 1699113600)]
        public void TestToEpochTime(string dtStr, uint expected)
        {
            // Act
            var dt = DateTime.ParseExact(dtStr, "yyyy-MM-dd", CultureInfo.CurrentCulture);
            var actual = dt.ToEpochTime();

            // Assert
            Assert.Equal(expected, actual);
        }

        [Theory]
        [InlineData("2020-01-05 12:56:41", "2020-01-05")]
        [InlineData("2020-05-11 13:12:33", "2020-05-11")]
        [InlineData("2020-09-04 03:45:21", "2020-09-04")]
        [InlineData("2023-11-05 21:14:58", "2023-11-05")]
        public void TestStartDate(string dtStr, string expected)
        {
            // Act
            var dt = DateTime.ParseExact(dtStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture);
            var actual = CommonUtil.GetStartOfDay(dt);

            // Assert
            Assert.Equal(expected, actual.ToString("yyyy-MM-dd"));
        }

        [Theory]
        [InlineData("2020-01-05 12:56:41", "Sunday, January 5, 2020 11:59:59 PM")]
        [InlineData("2020-05-11 13:12:33", "Monday, May 11, 2020 11:59:59 PM")]
        [InlineData("2020-09-04 03:45:21", "Friday, September 4, 2020 11:59:59 PM")]
        [InlineData("2023-11-05 21:14:58", "Sunday, November 5, 2023 11:59:59 PM")]
        public void TestEndDate(string dtStr, string expected)
        {
            // Act
            var dt = DateTime.ParseExact(dtStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.CurrentCulture);
            var actual = CommonUtil.GetEndOfDay(dt).ToString("F");

            // Assert
            Assert.Equal(expected, actual);
        }

        [Theory]
        [InlineData("123e4567-e89b-12d3-a456-426652340000", true)]
        [InlineData("187a5c91-abfc-482d-f85a-863542138462", true)]
        [InlineData("Yo Hommie!", false)]
        [InlineData("", false)]
        public void TestIsGuid(string guid, bool expected)
        {
            // Act
            var actual = CommonUtil.IsGuid(guid);

            // Assert
            Assert.Equal(expected, actual);
        }

        [Theory]
        [InlineData(0.125, 0, 0.125)]
        [InlineData(5.846, 5, 0.846)]
        [InlineData(10.256, 10, 0.256)]
        [InlineData(256.14578569, 256, 0.14578569)]
        public void TestWholeAndRemainder(decimal dec, int integral, decimal remainder)
        {
            // Act
            var actual = CommonUtil.GetWholeAndRemainder(dec);

            // Assert
            Assert.Equal(integral, actual.Item1); 
            Assert.Equal(remainder, actual.Item2);
        }

        [Theory]
        [InlineData("123e4567-e89b-12d3-a456-426652340000", "1314564453825188563")]
        [InlineData("187a5c91-abfc-482d-f85a-863542138462", "1763823984794421293")]
        public void TestGetExtOrderNoFromGuid(Guid guid, string expected)
        {
            // Act
            var actual = CommonUtil.GetExtOrderNoFromOrderId(guid);

            // Assert
            Assert.Equal(expected, actual);
        }

        [Fact]
        public void TestCsvString()
        {
            // Arrange
            var csv = new CsvMsgTag();
            csv.SetMsgTag(TagMsgId.ProdCode, "Bitcoin");
            csv.SetMsgTag(TagMsgId.Price, 65847);
            csv.SetMsgTag(TagMsgId.BuySell, "B");
            csv.SetMsgTag(TagMsgId.Qty, 1.5M, true);

            // Act
            var actual = csv.GetCsvMsgTagString(protocol);
            var expected = "5105,,,,,,,,,Bitcoin,B,65847,1500000,,,,,,,,,,,,,\r\n";

            // Assert
            Assert.Equal(expected, actual);

            // output
            _testOut.WriteLine(actual);
        }

        [Fact]
        public void TestJsonString()
        {
            // Arrange
            var csv = new CsvMsgTag();
            csv.SetMsgTag(TagMsgId.ProdCode, "Bitcoin");
            csv.SetMsgTag(TagMsgId.Price, 65847);
            csv.SetMsgTag(TagMsgId.BuySell, "B");
            csv.SetMsgTag(TagMsgId.Qty, 1.5M, true);

            // Act
            var actual = csv.ToJsonString();
            var actual_indented = csv.ToJsonString(Formatting.Indented);
            var expected = "{\"$id\":\"1\",\"ProdId\":\"\",\"Multiplier\":0.000001}";

            // Assert
            Assert.Equal(expected, actual);

            // output
            _testOut.WriteLine(actual_indented);
        }

        [Fact(Skip = "No Test for now")]
        public void TestFromJsonToObject()
        {
            try
            {
                // Arrange
                SQLite.Instance.Init();
                PersistantCache.Instance.InsertServerMsg(5105, "3,4,5105,22,1,2,3,77,15,700,16,17,711,18,22,649,29,712,78,25,23,689,584,624,629,745,87");

                var mp = new ModelParser();
                var md = mp.InterpretData("5105,444,1,AccNo2,450,,,UBTC,B,50270,200000,22,649,29,712,78,0,23,689,584,624,CLIENT-ORDER-ID,745,87");

                // Act
                var json = md.csvMsgTag.GetCsvMsgTagString(protocol);

                var actual = json.FromJsonToObject<CsvMsgTag>();
                var expected = md.csvMsgTag;

                // Assert
                Assert.Equal(expected, actual);
            }
            finally
            {
                SQLite.Instance.Term();
            }            
        }

        [Theory]
        [InlineData("2020-07-30", "BUY", "FILE", "Object", "Secret", "2bda2085ca081d8ad13e66b7a1eb33863a724777f54c0e428e4887c3806abc54")]
        [InlineData("2020-05-02", "Start", "DB", "HttpResponse", "ApiKey", "05a568935d5f982eaef5364dafec60bca7576be49a18dcf16caa19134bbb8129")]
        [InlineData("2020-06-29", "End", "Memory", "HTML5", "Passphrase", "84f5eb0d00ebbc7ace2fb2db944ef7d423a2221d3a2ceb7be8e709bbbf1404ff")]
        public void TestGetAccessSign(string timestamp, string command, string path, string body, string apiSecret, string expected)
        {
            // Act
            var actual = CommonUtil.GetAccessSign(timestamp, command, path, body, apiSecret);

            // Assert
            Assert.Equal(expected, actual);

            // output
            _testOut.WriteLine(actual);
        }

        [Theory]
        [InlineData("2020-02-02", "CRC20", "http://www.google.com/", "Search", "Password", "8a6476e3d98be33f9a3ca0451f2aa6d0566055611e35aadc0fdae9b53c23426d")]
        [InlineData("2020-03-21", "MD5", "http://www.microsoft.com/", "OS", "SecretPhase", "30fac615698dfc9a0fdc11728e8a14b0e3cd9c2639bfa11d6d6519c4e04c2c15")]
        [InlineData("2020-04-30", "SHA256", "http://www.nvidia.com/", "Chips", "Key", "ea611979147e7eb1d6d097a59d3de584ef823a4a7e44f2dd595c6b599093fcca")]
        public void TestGenerateSignature(string timestamp, string method, string url, string body, string apiSecret, string expected)
        {
            // Act
            var actual = CommonUtil.GenerateSignature(timestamp, method, url, body, apiSecret);

            // Assert
            Assert.Equal(expected, actual);

            // output
            _testOut.WriteLine(actual);
        }
    }
}