using System;
using Xunit;
using Newtonsoft.Json.Linq;
using FluentAssertions;

namespace raven_integration
{

    public class UserCrud
    {

        /// <summary>
        /// Test all CRUD routes for a User
        /// </summary>       
        [Fact]
        public async void CRUD()
        {

            //CREATE
            dynamic D1 = new JObject();
            D1.name = Util.Uniquify("First Test User");
            D1.ownerId = 1L;
            D1.active = true;
            D1.login = Util.Uniquify("LOGIN");
            D1.password = Util.Uniquify("PASSWORD");
            D1.roles = 0;//norole
            D1.localeId = 1;//random locale
            D1.userType = 3;//non scheduleable

            ApiResponse R1 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D1.ToString());
            Util.ValidateDataReturnResponseOk(R1);
            long d1Id = R1.ObjectResponse["data"]["id"].Value<long>();


            dynamic D2 = new JObject();
            D2.name = Util.Uniquify("Second Test User");
            D2.ownerId = 1L;
            D2.active = true;
            D2.login = Util.Uniquify("LOGIN");
            D2.password = Util.Uniquify("PASSWORD");
            D2.roles = 0;//norole
            D2.localeId = 1;//random locale
            D2.userType = 3;//non scheduleable

            ApiResponse R2 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D2.ToString());
            Util.ValidateDataReturnResponseOk(R2);
            long d2Id = R2.ObjectResponse["data"]["id"].Value<long>();


            //RETRIEVE

            //Get one
            ApiResponse R3 = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateDataReturnResponseOk(R3);
            R3.ObjectResponse["data"]["name"].Value<string>().Should().Be(D2.name.ToString());



            //UPDATE
            //PUT

            //update w2id 
            D2.name = Util.Uniquify("UPDATED VIA PUT SECOND TEST User");
            D2.OwnerId = 1;
            D2.concurrencyToken = R2.ObjectResponse["data"]["concurrencyToken"].Value<uint>();
            ApiResponse PUTTestResponse = await Util.PutAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), D2.ToString());
            Util.ValidateHTTPStatusCode(PUTTestResponse, 200);

            //check PUT worked
            ApiResponse checkPUTWorked = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateNoErrorInResponse(checkPUTWorked);
            checkPUTWorked.ObjectResponse["data"]["name"].Value<string>().Should().Be(D2.name.ToString());
            uint concurrencyToken = PUTTestResponse.ObjectResponse["data"]["concurrencyToken"].Value<uint>();

            //PATCH
            var newName = Util.Uniquify("UPDATED VIA PATCH SECOND TEST User");
            string patchJson = "[{\"value\": \"" + newName + "\",\"path\": \"/name\",\"op\": \"replace\"}]";
            ApiResponse PATCHTestResponse = await Util.PatchAsync("User/" + d2Id.ToString() + "/" + concurrencyToken.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateHTTPStatusCode(PATCHTestResponse, 200);

            //check PATCH worked
            ApiResponse checkPATCHWorked = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateNoErrorInResponse(checkPATCHWorked);
            checkPATCHWorked.ObjectResponse["data"]["name"].Value<string>().Should().Be(newName);

            //DELETE
            ApiResponse DELETETestResponse = await Util.DeleteAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateHTTPStatusCode(DELETETestResponse, 204);
        }


        /// <summary>
        /// Test not found
        /// </summary>       
        [Fact]
        public async void GetNonExistentItemShouldError()
        {
            //Get non existant
            //Should return status code 404, api error code 2010
            ApiResponse R = await Util.GetAsync("User/999999", await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateResponseNotFound(R);
        }

        /// <summary>
        /// Test bad modelstate
        /// </summary>       
        [Fact]
        public async void GetBadModelStateShouldError()
        {
            //Get non existant
            //Should return status code 400, api error code 2200 and a first target in details of "id"
            ApiResponse R = await Util.GetAsync("User/2q2", await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateBadModelStateResponse(R, "id");
        }


        /// <summary>
        /// 
        /// </summary>       
        [Fact]
        public async void PutConcurrencyViolationShouldFail()
        {
            //CREATE          
            dynamic D = new JObject();
            D.name = Util.Uniquify("PutConcurrencyViolationShouldFail");
            D.ownerId = 1L;
            D.active = true;
            D.login = Util.Uniquify("LOGIN");
            D.password = Util.Uniquify("PASSWORD");
            D.roles = 0;//norole
            D.localeId = 1;//random locale
            D.userType = 3;//non scheduleable

            ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);
            long D1Id = R.ObjectResponse["data"]["id"].Value<long>();
            uint OriginalConcurrencyToken = R.ObjectResponse["data"]["concurrencyToken"].Value<uint>();


            //UPDATE
            //PUT

            D.name = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATE VIA PUT ");
            D.concurrencyToken = OriginalConcurrencyToken - 1;//bad token
            ApiResponse PUTTestResponse = await Util.PutAsync("User/" + D1Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateConcurrencyError(PUTTestResponse);
        }


        /// <summary>
        /// 
        /// </summary>       
        [Fact]
        public async void PatchConcurrencyViolationShouldFail()
        {
            //CREATE
            dynamic D = new JObject();
            D.name = Util.Uniquify("PatchConcurrencyViolationShouldFail");
            D.ownerId = 1L;
            D.active = true;
            D.login = Util.Uniquify("LOGIN");
            D.password = Util.Uniquify("PASSWORD");
            D.roles = 0;//norole
            D.localeId = 1;//random locale
            D.userType = 3;//non scheduleable

            ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);
            long w2Id = R.ObjectResponse["data"]["id"].Value<long>();
            uint OriginalConcurrencyToken = R.ObjectResponse["data"]["concurrencyToken"].Value<uint>();


            //PATCH
            var newName = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATED VIA PATCH");
            string patchJson = "[{\"value\": \"" + newName + "\",\"path\": \"/name\",\"op\": \"replace\"}]";
            ApiResponse PATCHTestResponse = await Util.PatchAsync("User/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateConcurrencyError(PATCHTestResponse);
        }



        /// <summary>
        /// 
        /// </summary>       
        [Fact]
        public async void DisallowedPatchAttemptsShouldFail()
        {
            //CREATE
            dynamic D = new JObject();
            D.name = Util.Uniquify("DisallowedPatchAttemptsShouldFail");
            D.ownerId = 1L;
            D.active = true;
            D.login = Util.Uniquify("LOGIN");
            D.password = Util.Uniquify("PASSWORD");
            D.roles = 0;//norole
            D.localeId = 1;//random locale
            D.userType = 3;//non scheduleable

            ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);
            long w2Id = R.ObjectResponse["data"]["id"].Value<long>();
            uint OriginalConcurrencyToken = R.ObjectResponse["data"]["concurrencyToken"].Value<uint>();


            //PATCH attempt on Id           
            string patchJson = "[{\"value\": \"0\",\"path\": \"/id\",\"op\": \"replace\"}]";
            ApiResponse PATCHTestResponse = await Util.PatchAsync("User/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateErrorCodeResponse(PATCHTestResponse, 2200, 400);

            //PATCH attempt on OwnerId           
            patchJson = "[{\"value\": \"0\",\"path\": \"/ownerid\",\"op\": \"replace\"}]";
            PATCHTestResponse = await Util.PatchAsync("User/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateErrorCodeResponse(PATCHTestResponse, 2200, 400);

            //PATCH attempt add field         
            patchJson = "[{\"value\": \"0\",\"path\": \"/bogus\",\"op\": \"add\"}]";
            PATCHTestResponse = await Util.PatchAsync("User/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateErrorCodeResponse(PATCHTestResponse, 2200, 400);

            //PATCH attempt remove name field         
            patchJson = "[{\"path\": \"/name\",\"op\": \"remove\"}]";
            PATCHTestResponse = await Util.PatchAsync("User/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateErrorCodeResponse(PATCHTestResponse, 2200, 400);
        }


        /// <summary>
        /// 
        /// </summary>       
        [Fact]
        public async void PatchPasswordShouldWork()
        {
            //CREATE
            dynamic D = new JObject();
            D.name = Util.Uniquify("PatchPasswordShouldWork");
            D.ownerId = 1L;
            D.active = true;
            D.login = Util.Uniquify("LOGIN");
            D.password = Util.Uniquify("PASSWORD");
            D.roles = 0;//norole
            D.localeId = 1;//random locale
            D.userType = 3;//non scheduleable

            ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);
            long UserId = R.ObjectResponse["data"]["id"].Value<long>();
            uint OriginalConcurrencyToken = R.ObjectResponse["data"]["concurrencyToken"].Value<uint>();

            //Test can login
            dynamic DCreds = new JObject();
            DCreds.password = D.password;
            DCreds.login = D.login;
            R = await Util.PostAsync("Auth", null, DCreds.ToString());
            Util.ValidateDataReturnResponseOk(R);

            //PUT
            var NewPassword = "NEW_PASSWORD";
            D.password = NewPassword;
            D.concurrencyToken = OriginalConcurrencyToken;
            R = await Util.PutAsync("User/" + UserId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);

            //Test can login with new creds
            //dynamic DCreds = new JObject();
            DCreds.password = NewPassword;
            DCreds.login = D.login;
            R = await Util.PostAsync("Auth", null, DCreds.ToString());
            Util.ValidateDataReturnResponseOk(R);
        }


        /// <summary>
        /// 
        /// </summary>       
        [Fact]
        public async void PutPasswordShouldWork()
        {
            //CREATE
            dynamic D = new JObject();
            D.name = Util.Uniquify("PutPasswordShouldWork");
            D.ownerId = 1L;
            D.active = true;
            D.login = Util.Uniquify("LOGIN");
            D.password = Util.Uniquify("PASSWORD");
            D.roles = 0;//norole
            D.localeId = 1;//random locale
            D.userType = 3;//non scheduleable

            ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
            Util.ValidateDataReturnResponseOk(R);
            long UserId = R.ObjectResponse["data"]["id"].Value<long>();
            uint OriginalConcurrencyToken = R.ObjectResponse["data"]["concurrencyToken"].Value<uint>();

            //Test can login
            dynamic DCreds = new JObject();
            DCreds.password = D.password;
            DCreds.login = D.login;
            R = await Util.PostAsync("Auth", null, DCreds.ToString());
            Util.ValidateDataReturnResponseOk(R);

            //PATCH
            var newPassword = "NEW_PASSWORD";
            string patchJson = "[{\"value\": \"" + newPassword + "\",\"path\": \"/password\",\"op\": \"replace\"}]";
            R = await Util.PatchAsync("User/" + UserId.ToString() + "/" + OriginalConcurrencyToken.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
            Util.ValidateDataReturnResponseOk(R);

            //Test can login with new creds
            //dynamic DCreds = new JObject();
            DCreds.password = newPassword;
            DCreds.login = D.login;
            R = await Util.PostAsync("Auth", null, DCreds.ToString());
            Util.ValidateDataReturnResponseOk(R);
        }




        /// <summary>
        /// 
        /// </summary>
        [Fact]
        public async void UserListFilterAndSortWorks()
        {

            var ObjectNameStart = Util.Uniquify("UserListFilterAndSortWorks");

            //CREATE 3 TEST WIDGETS TO TEST ORDER
            long FirstInOrdertId = 0;
            long SecondInOrderId = 0;
            long ThirdInOrderId = 0;

            dynamic d = new JObject();
            d.name = Util.Uniquify(ObjectNameStart);
            d.active = false;
            d.login = Util.Uniquify("LOGIN");
            d.password = Util.Uniquify("PASSWORD");
            d.roles = 0;//norole
            d.localeId = 1;//random locale
            d.userType = 3;//non scheduleable

            ApiResponse a = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
            Util.ValidateDataReturnResponseOk(a);
            FirstInOrdertId = a.ObjectResponse["data"]["id"].Value<long>();

            d = new JObject();
            d.name = Util.Uniquify(ObjectNameStart);
            d.login = Util.Uniquify("LOGIN");
            d.password = Util.Uniquify("PASSWORD");
            d.roles = 0;//norole
            d.localeId = 1;//random locale
            d.userType = 3;//non scheduleable
            d.active = true;

            a = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
            Util.ValidateDataReturnResponseOk(a);
            ThirdInOrderId = a.ObjectResponse["data"]["id"].Value<long>();

            d = new JObject();
            d.name = Util.Uniquify(ObjectNameStart);
            d.login = Util.Uniquify("LOGIN");
            d.password = Util.Uniquify("PASSWORD");
            d.roles = 0;//norole
            d.localeId = 1;//random locale
            d.userType = 3;//non scheduleable
            d.active = false;
            a = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
            Util.ValidateDataReturnResponseOk(a);
            SecondInOrderId = a.ObjectResponse["data"]["id"].Value<long>();


            //CREATE FILTER
            d = new JObject();
            d.name = Util.Uniquify(ObjectNameStart);
            d["public"] = true;
            d.listKey = "user";

            //FILTER IN BY NAME FOR TESTING THIS RUN ONLY
            dynamic dfilter = new JArray();
            //name starts with filter to constrict to widgets that this test block created only
            dynamic DataFilterNameStart = new JObject();
            DataFilterNameStart.fld = "name";
            DataFilterNameStart.op = Util.OpStartsWith;
            DataFilterNameStart.value = ObjectNameStart;
            dfilter.Add(DataFilterNameStart);
            d.filter = dfilter.ToString();

            dynamic dsortarray = new JArray();

            //SORT ORDER ###################
            //sort by active then by ID 

            dynamic dsort = new JObject();
            dsort.fld = "active";
            dsort.dir = "+";
            dsortarray.Add(dsort);

            dsort = new JObject();
            dsort.fld = "id";
            dsort.dir = "+";
            dsortarray.Add(dsort);

            d.sort = dsortarray.ToString();

            a = await Util.PostAsync("DataFilter", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
            Util.ValidateDataReturnResponseOk(a);

            long DataFilterId = a.ObjectResponse["data"]["id"].Value<long>();

            //NOW FETCH WIDGET LIST WITH FILTER
            a = await Util.GetAsync($"User/listusers?Offset=0&Limit=999&DataFilterId={DataFilterId.ToString()}", await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateDataReturnResponseOk(a);
            Util.ValidateHTTPStatusCode(a, 200);

            //assert contains exactly 3 records
            ((JArray)a.ObjectResponse["data"]).Count.Should().Be(3);

            //assert the order returned
            a.ObjectResponse["data"][0]["id"].Value<long>().Should().Be(FirstInOrdertId);
            a.ObjectResponse["data"][1]["id"].Value<long>().Should().Be(SecondInOrderId);
            a.ObjectResponse["data"][2]["id"].Value<long>().Should().Be(ThirdInOrderId);


            a = await Util.DeleteAsync("User/" + FirstInOrdertId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateHTTPStatusCode(a, 204);

            a = await Util.DeleteAsync("User/" + SecondInOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateHTTPStatusCode(a, 204);

            a = await Util.DeleteAsync("User/" + ThirdInOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateHTTPStatusCode(a, 204);

            //DELETE DATAFILTER
            a = await Util.DeleteAsync("DataFilter/" + DataFilterId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
            Util.ValidateHTTPStatusCode(a, 204);

        }


        //==================================================

    }//eoc
}//eons
