using Newtonsoft.Json;
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Threading.Tasks;
using TINK.Model.Connector;
using TINK.Model.Services.CopriApi;
using TINK.Repository;
using TINK.Repository.Response;

namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{

    [TestFixture]
    public class TestCachedQueryLoggedIn
    {
        private const string BIKESAVAILABLE = @"{
              ""copri_version"" : ""4.1.0.0"",
              ""bikes"" : {},
              ""response_state"" : ""OK"",
              ""apiserver"" : ""https://app.tink-konstanz.de"",
              ""authcookie"" : """",
              ""response"" : ""bikes_available"",
              ""bikes"" : {
                                ""2352"" : {
                                ""description"" : ""Cargo Long"",
                                ""state"" : ""available"",
                                ""bike"" : ""1"",
                                ""gps"" : { ""latitude"": ""47.669888"", ""longitude"": ""9.167749"" },
                                ""station"" : ""9""
                                }
            }
        }";

        private const string BIKESAVAILABLEEMPTY = @"{
              ""copri_version"" : ""4.1.0.0"",
              ""bikes"" : {},
              ""response_state"" : ""OK"",
              ""apiserver"" : ""https://app.tink-konstanz.de"",
              ""authcookie"" : """",
              ""response"" : ""bikes_available"",
              ""bikes"" : {
            }
        }";

        private const string BIKESOCCUPIED = @"{
          ""authcookie"" : ""6103_f782a208d9399291ba8d086b5dcc2509_12345678"",
          ""debuglevel"" : ""2"",
          ""user_group"" : [ ""TINK"" ],
          ""user_id"" : ""javaminister@gmail.com"",
          ""response"" : ""user_bikes_occupied"",
          ""response_state"" : ""OK"",
          ""response_text"" : ""Die Liste der reservierten und gebuchten Fahrräder wurde erfolgreich geladen"",
          ""apiserver"" : ""https://tinkwwp.copri-bike.de"",
          ""bikes_occupied""  : {
             ""89004"" : {
                ""start_time"" : ""2018-01-27 17:33:00.989464+01"",
                ""station"" : ""9"",
                ""unit_price"" : ""2.00"",
                ""tariff_description"": {
                            ""free_hours"" : ""0.5"",
                            ""name"" : ""TINK Tarif"",
                            ""max_eur_per_day"" : ""9.00""
                },                
                ""timeCode"" : ""2061"",
                ""description"" : ""Cargo Long"",
                ""bike"" : ""4"",
                ""total_price"" : ""20.00"",
                ""state"" : ""requested"",
                ""real_hours"" : ""66.05"",
                ""bike_group"" : [ ""TINK"" ],
                ""now_time"" : ""2018-01-30 11:36:45"",
                ""request_time"" : ""2018-01-27 17:33:00.989464+01"",
                ""computed_hours"" : ""10.0""
              }
            }
        }";

        private const string STATIONSALL = @"{
              ""copri_version"" : ""4.1.0.0"",
              ""stations"" : {
                 ""5"" : {
                    ""station"" : ""5"",
                    ""bike_soll"" : ""0"",
                    ""bike_ist"" : ""7"",
                    ""station_group"" : [ ""TINK"" ],
                    ""gps"" : { ""latitude"": ""47.66756"", ""longitude"": ""9.16477"" },
                    ""state"" : ""available"",
                    ""description"" : """"
                 },
                 ""13"" : {
                    ""station"" : ""13"",
                    ""bike_soll"" : ""4"",
                    ""bike_ist"" : ""1"",
                    ""station_group"" : [ ""TINK"" ],
                    ""gps"" : { ""latitude"": ""47.657756"", ""longitude"": ""9.176084"" },
                    ""state"" : ""available"",
                    ""description"" : """"
                 },
                 ""30"" : {
                    ""station"" : ""30"",
                    ""bike_soll"" : ""5"",
                    ""bike_ist"" : ""0"",
                    ""station_group"" : [ ""TINK"", ""Konrad"" ],
                    ""gps"" : { ""latitude"": ""47.657766"", ""longitude"": ""9.176094"" },
                    ""state"" : ""available"",
                    ""description"" : ""Test für Stadtradstation""
                 }
        },
              ""user_group"" : [ ""Konrad"", ""TINK"" ],
              ""response_state"" : ""OK"",
              ""authcookie"" : ""6103_f782a208d9399291ba8d086b5dcc2509_12345678"",
              ""debuglevel"" : ""2"",
              ""response"" : ""stations_all"",
              ""user_id"" : ""javaminister@gmail.com"",
              ""apiserver"" : ""https://tinkwwp.copri-bike.de""
        }";

        private const string STATIONSALLEMPTY = @"{
              ""copri_version"" : ""4.1.0.0"",
              ""stations"" : {
        },
              ""user_group"" : [ ""Konrad"", ""TINK"" ],
              ""response_state"" : ""OK"",
              ""authcookie"" : ""6103_f782a208d9399291ba8d086b5dcc2509_12345678"",
              ""debuglevel"" : ""2"",
              ""response"" : ""stations_all"",
              ""user_id"" : ""javaminister@gmail.com"",
              ""apiserver"" : ""https://tinkwwp.copri-bike.de""
        }";

        [Test]
        public async Task TestGetStations_StationsFromCache()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL),
                new System.Exception("Bang when getting stations..."))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();

            Assert.AreEqual(3, result.Response.StationsAll.Count);
            Assert.AreEqual(2, result.Response.Bikes.Count);
            Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
            Assert.AreEqual("Bang when getting stations...", result.Exception.Message);
        }

        [Test]
        public async Task TestGetStations_BikesAvailableFromCache()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALLEMPTY))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE),
                new System.Exception("Bang when getting bikes..."))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();

            Assert.AreEqual(3, result.Response.StationsAll.Count);
            Assert.AreEqual(2, result.Response.Bikes.Count);
            Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
            Assert.AreEqual("Bang when getting bikes...", result.Exception.Message);
        }

        [Test]
        public async Task TestGetStations_BikesOccupiedFromCache()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALLEMPTY))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLEEMPTY))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED),
                new System.Exception("Bang when getting bikes occupied..."))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();

            Assert.AreEqual(3, result.Response.StationsAll.Count);
            Assert.AreEqual(2, result.Response.Bikes.Count);
            Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
            Assert.AreEqual("Bang when getting bikes occupied...", result.Exception.Message);
        }

        [Test]
        public async Task TestGetStations()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            server.Stub(x => x.AddToCache(Arg<Result<StationsAllResponse>>.Is.Anything));
            server.Stub(x => x.AddToCache(Arg<Result<BikesAvailableResponse>>.Is.Anything));
            server.Stub(x => x.AddToCache(Arg<Result<BikesReservedOccupiedResponse>>.Is.Anything));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();

            Assert.AreEqual(3, result.Response.StationsAll.Count);
            Assert.AreEqual(2, result.Response.Bikes.Count);
            Assert.AreEqual(typeof(CopriCallsHttps), result.Source);
            Assert.IsNull(result.Exception);
        }

        [Test]
        public async Task TestGetBikes_BikesAvailableFromCache()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetBikesAvailable()).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE),
                new System.Exception("Bang, bikes avail..."))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAsync();

            Assert.AreEqual(2, result.Response.Count);
            Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
            Assert.AreEqual("Bang, bikes avail...", result.Exception.Message);
        }

        [Test]
        public async Task TestGetBikes_BikesOccupiedFromCache()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLEEMPTY))));

            server.Stub(x => x.GetBikesOccupied(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED),
                new System.Exception("Bang, error bikes occupied"))));

            server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsMonkeyStore),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAsync();

            Assert.AreEqual(2, result.Response.Count);
            Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
            Assert.AreEqual("Bang, error bikes occupied", result.Exception.Message);
        }

        [Test]
        public async Task TestGetBikes()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetBikesAvailable()).Return(Task.Run(() => new Result<BikesAvailableResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));

            server.Stub(x => x.GetBikesOccupied()).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            server.Stub(x => x.AddToCache(Arg<Result<BikesAvailableResponse>>.Is.Anything));
            server.Stub(x => x.AddToCache(Arg<Result<BikesReservedOccupiedResponse>>.Is.Anything));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAsync();

            Assert.AreEqual(2, result.Response.Count);
            Assert.AreEqual(typeof(CopriCallsHttps), result.Source);
            Assert.IsNull(result.Exception);
        }

        [Test]
        public async Task TestGetBikesOccupied()
        {
            var server = MockRepository.GenerateMock<ICachedCopriServer>();

            server.Stub(x => x.GetBikesOccupied()).Return(Task.Run(() => new Result<BikesReservedOccupiedResponse>(
                typeof(CopriCallsHttps),
                JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));

            server.Stub(x => x.AddToCache(Arg<Result<BikesReservedOccupiedResponse>>.Is.Anything));

            var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesOccupiedAsync();

            Assert.AreEqual(1, result.Response.Count);
            Assert.AreEqual(typeof(CopriCallsHttps), result.Source);
            Assert.IsNull(result.Exception);
        }
    }
}