using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Winbooks.Serializer;
using Winbooks.TORM.OM;

namespace Winbooks.Apis.Services
{
    /// <summary>
    /// Base class of entity
    /// </summary>
    public abstract class BaseEntities
    {
        private UserCredential _userCredential;
        /// <summary>
        /// Gets the authentication
        /// </summary>
        public UserCredential Services { get { return this._userCredential; } }

        private string _folderCode;
        /// <summary>
        /// Gets the folder code is working with
        /// </summary>
        public string FolderCode { get { return this._folderCode; } }

        private BaseEntitySetting _setting;
        /// <summary>
        /// Get and set setting of entity
        /// </summary>
        public BaseEntitySetting Setting
        {
            get { return this._setting; }
            set
            {
                this._setting = value;
            }
        }

        private Dictionary<string, string> _dicTranslateId = new Dictionary<string, string>();

        public Dictionary<string, string> DicTranslateId
        {
            get { return _dicTranslateId; }
            set { _dicTranslateId = value; }
        }
        /// <summary>
        /// Base class of Winbooks api entities
        /// </summary>
        public BaseEntities(UserCredential userCredential, string folderCode)
        {
            this._userCredential = userCredential;
            this._folderCode = folderCode;
            this.Setting = new BaseEntitySetting();
            this.CheckingAccess();
        }

        #region ** Get **

        /// <summary>
        /// GetByCode uses for all the entities has code
        /// </summary>
        /// <typeparam name="T">the type of the entity  Ex: Customer, Supplier</typeparam>
        /// <param name="entityCode">the code of the entity</param>
        /// <returns>the entity</returns>
        public T GetByCode<T>(string entityCode) where T : BaseOM
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "/", entityCode, "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter, this.Setting);
            IDictionary<string, string> result = _userCredential.GetAsyncWithChunkingData(url);
            result = ProcessTranslateId(this.DicTranslateId, result);
            if (result.Any(prop => prop.Key.Split('|').Length > 1))
            {
                throw new Exception("There's a key value not suitable for single om");
            }
            if (result != null && result.Any())
            {
                var t = (T)WinbooksConverter.FromJsonToWinbooksObject(result, typeof(T));
                return t;
            }
            return default(T);
        }

        private T GetByInputUrl<T>(string inputUrl) where T : BaseOM
        {
            IDictionary<string, string> result = _userCredential.GetAsyncWithChunkingData(inputUrl);
            result = ProcessTranslateId(this.DicTranslateId, result);
            if (result.Any(prop => prop.Key.Split('|').Length > 1))
            {
                throw new Exception("There's a key value not suitable for single om");
            }
            if (result != null && result.Any())
            {
                var t = (T)WinbooksConverter.FromJsonToWinbooksObject(result, typeof(T));
                return t;
            }
            return default(T);
        }

        /// <summary>
        /// GetByCode uses for all the entities has code
        /// </summary>
        /// <typeparam name="T">the type of the entity  Ex: Customer, Supplier</typeparam>
        /// <param name="entityCode">the code of the entity</param>
        /// <param name="entityLevel">the level entity will be serialized and return</param>
        /// <returns></returns>
        public T GetByCode<T>(string entityCode, int entityLevel) where T : BaseOM
        {
            this.Setting.GetEntityLevel = entityLevel;
            return GetByCode<T>(entityCode);
        }

        /// <summary>
        /// GetByCode uses for all the entities has code
        /// </summary>
        /// <typeparam name="T">the type of the entity  Ex: Customer, Supplier</typeparam>
        /// <param name="entityCode">the code of the entity</param>
        /// <param name="IsShowId">Indcate result will have id of server or not</param>
        /// <returns></returns>
        public T GetByCode<T>(string entityCode, bool IsShowId) where T : BaseOM
        {
            this.Setting.IsShowId = IsShowId;
            return GetByCode<T>(entityCode);
        }

        /// <summary>
        /// GetByCode uses for all the entities has code
        /// </summary>
        /// <typeparam name="T">the type of the entity  Ex: Customer, Supplier</typeparam>
        /// <param name="entityCode">the code of the entity</param>
        /// <param name="entityLevel">the level entity will be serialized and return</param>
        /// <param name="IsShowId">Indcate result will have id of server or not</param>
        /// <returns></returns>
        public T GetByCode<T>(string entityCode, int entityLevel, bool IsShowId) where T : BaseOM
        {
            this.Setting.IsShowId = IsShowId;
            this.Setting.GetEntityLevel = entityLevel;
            return GetByCode<T>(entityCode);
        }

        /// <summary>
        /// GetByCode uses for all the entities has code
        /// </summary>
        /// <typeparam name="T">the type of the entity  Ex: Customer, Supplier</typeparam>
        /// <param name="entityCode">the code of the entity</param>
        /// <param name="setting">setting of base Entity</param>
        /// <returns></returns>
        public T GetByCode<T>(string entityCode, BaseEntitySetting setting) where T : BaseOM
        {
            this.Setting = setting;
            return GetByCode<T>(entityCode);
        }

        /// <summary>
        /// GetById uses for all the enties has id or code
        /// </summary>
        /// <typeparam name="T">type of the entity</typeparam>
        /// <param name="entityId">id of the entity in Guid</param>
        /// <returns>the entity</returns>
        public T GetById<T>(Guid entityId) where T : BaseOM
        {
            return GetById<T>(entityId.ToString());
        }

        /// <summary>
        /// GetById uses for all the enties has id or code
        /// </summary>
        /// <typeparam name="T">type of the entity</typeparam>
        /// <param name="entityId">id of the entity in string</param>
        /// <returns>the entity</returns>
        public T GetById<T>(string entityId) where T : BaseOM
        {
            string entityName = typeof(T).Name;

            string parameter = String.Concat("/", entityName, "/", entityId, "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter, this.Setting);
            var result = _userCredential.GetAsyncWithChunkingData(url);
            result = ProcessTranslateId(this.DicTranslateId, result);
            if (result != null && result.Any())
            {
                T t = (T)WinbooksConverter.FromJsonToWinbooksObject(result, typeof(T));
                return t;
            }
            return default(T);
        }

        /// <summary>
        /// GetById uses for all the enties has id or code
        /// </summary>
        /// <typeparam name="T">type of the entity</typeparam>
        /// <param name="entitiyId">id of the entity in string</param>
        /// <param name="isShowId">result return with id or not</param>
        /// <returns></returns>
        public T GetById<T>(string entitiyId, bool isShowId) where T : BaseOM
        {
            this.Setting.IsShowId = isShowId;
            return GetById<T>(entitiyId);
        }

        /// <summary>
        /// Get all the data of the entity
        /// </summary>
        /// <typeparam name="T">type of the entity : Customer, Supplier</typeparam>
        /// <returns></returns>
        public IList<T> GetAll<T>() where T : BaseOM
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter, this.Setting);
            IDictionary<string, string> result = _userCredential.GetAsyncWithChunkingData(url);
            result = ProcessTranslateId(this.DicTranslateId, result);
            if (result != null && result.Any())
            {
                IList<T> t = (IList<T>)WinbooksConverter.FromJsonStringToListObject<T>(result);
                return t;
            }
            return null;
        }

        public IDictionary<string, string> ProcessTranslateId(Dictionary<string, string> lstIdTranslated,
            IDictionary<string, string> currentResult)
        {
            if (lstIdTranslated == null || !lstIdTranslated.Any() || !this.Setting.IsTranslacteId)
                return currentResult;
            IDictionary<string, string> newResult = new Dictionary<string, string>();
            foreach (var crResult in currentResult)
            {
                string finalValue = crResult.Value;
                foreach (var idTranslate in lstIdTranslated)
                {
                    finalValue = finalValue.Replace(idTranslate.Key, idTranslate.Value);
                    //newResult.Add(crResult.Key,crResult.Value.Replace(idTranslate.Key, idTranslate.Value));
                }
                newResult.Add(crResult.Key, finalValue);
            }
            return newResult;
        }

        public object GetAll(Type objectType)
        {
            var method = typeof(BaseEntities).GetMethods().FirstOrDefault(imethod => imethod.Name == "GetAll" && imethod.IsGenericMethod);
            // MethodInfo method = typeof(BaseEntities).GetMethod("GetAll", BindingFlags.InvokeMethod);
            MethodInfo generic = method.MakeGenericMethod(objectType);
            return generic.Invoke(this, null);
        }

        public object GetAll(Type objectType, ResultState state)
        {
            this.Setting.StateGettingObj = state;
            return GetAll(objectType);
        }

        /// <summary>
        /// Get all the data of the entity
        /// </summary>
        /// <typeparam name="T">type of the entity : Customer, Supplier</typeparam>
        /// <param name="isShowId">the result will have id or not</param>
        /// <returns></returns>
        public IList<T> GetAll<T>(bool isShowId) where T : BaseOM
        {
            var holdCurrentSetting = this.Setting;
            this.Setting.IsShowId = isShowId;
            var lstGetAll = GetAll<T>();
            this.Setting = holdCurrentSetting;
            return lstGetAll;
        }

        /// <summary>
        /// Get all the data of the entity
        /// </summary>
        /// <typeparam name="T">type of the entity : Customer, Supplier</typeparam>
        /// <param name="entityLevel">the level will be return </param>
        /// <returns>List baseOM</returns>
        public IList<T> GetAll<T>(int entityLevel) where T : BaseOM
        {
            var holdTempValue = this.Setting.GetEntityLevel;
            this.Setting.GetEntityLevel = entityLevel;
            var result = GetAll<T>();
            this.Setting.GetEntityLevel = holdTempValue;
            return result;
        }

        /// <summary>
        /// Get all the data of the entity
        /// </summary>
        /// <typeparam name="T">type of the entity : Customer, Supplier</typeparam>
        /// <param name="entityLevel">the level will be return</param>
        /// <param name="isShowId">the result will have id or not</param>
        /// <returns></returns>
        public IList<T> GetAll<T>(int entityLevel, bool isShowId) where T : BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.IsShowId = isShowId;
            this.Setting.GetEntityLevel = entityLevel;
            var result = GetAll<T>();
            this.Setting = holdSetting;
            return result;
        }

        #endregion ** Get **

        #region ** Get with condition **
        /// <summary>
        /// Get the data with the filter
        /// </summary>
        /// <typeparam name="T">type of the entity</typeparam>
        /// <param name="criteria">the criteria to filter</param>
        /// <returns></returns>
        public T GetFilter<T>(Winbooks.TORM.DAL.ICriteria criteria)
        {
            string condition = (criteria == null) ? "" : criteria.JsonCriteriaSerialize();
            return GetFilter<T>(condition);
        }

        public T GetFilter<T>(Winbooks.TORM.DAL.ICriteria criteria, bool isShowId)
        {
            this.Setting.IsShowId = isShowId;
            return GetFilter<T>(criteria);
        }
        /// <summary>
        /// Get the data with the filter
        /// </summary>
        /// <typeparam name="T">type of the entity</typeparam>
        /// <param name="condition">the criteria is serialized in the string</param>
        /// <returns></returns>
        public T GetFilter<T>(string condition)
        {
            IList<T> list = GetFilterAll<T>(condition);

            if (list != null && list.Count > 0)
                return list[0];

            return default(T);
        }

        /// <summary>
        /// Get the data collection with the filter
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="criteria">the criteria to make the filter</param>
        /// <returns></returns>
        public IList<T> GetFilterAll<T>(Winbooks.TORM.DAL.ICriteria criteria)
        {
            string condition = (criteria == null) ? "" : criteria.JsonCriteriaSerialize();
            return GetFilterAll<T>(condition);
        }

        /// <summary>
        ///  Get the data collection with the filter
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="criteria">the criteria to make the filter</param>
        /// <param name="entityLevel">the level object will be return</param>
        /// <returns></returns>
        public IList<T> GetFilterAll<T>(Winbooks.TORM.DAL.ICriteria criteria, int entityLevel)
        {
            this.Setting.GetEntityLevel = entityLevel;
            return GetFilterAll<T>(criteria);
        }


        /// <summary>
        ///  Get the data collection with the filter
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="criteria">the criteria to make the filter</param>
        /// <param name="isShowId">result will have id or not</param>
        /// <returns></returns>
        public IList<T> GetFilterAll<T>(Winbooks.TORM.DAL.ICriteria criteria, bool isShowId)
        {
            this.Setting.IsShowId = isShowId;
            return GetFilterAll<T>(criteria);
        }

        public IList<T> GetFilterAll<T>(Winbooks.TORM.DAL.ICriteria criteria, int entityLevel, bool isShowId)
        {
            var holdSetting = this.Setting;
            this.Setting.IsShowId = isShowId;
            this.Setting.GetEntityLevel = entityLevel;
            var result = GetFilterAll<T>(criteria);
            this.Setting = holdSetting;
            return result;
        }

        /// <summary>
        /// Get the data collection with the filter
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="condition">the criteria is serialized in the string</param>
        /// <returns></returns>

        public IList<T> GetFilterAll<T>(string condition)
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter, this.Setting);
            var result = _userCredential.PostAsync(url, condition);
            result = ProcessTranslateId(this.DicTranslateId, result);
            if (result != null && result.Any())
            {
                IList<T> t = (IList<T>)WinbooksConverter.FromJsonStringToListObject<T>(result);
                return t;
            }

            return null;
        }

        public JArray GetFilterAll(string condition, string objectName)
        {
            string entityName = objectName;
            string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter, this.Setting);
            var result = _userCredential.PostAsync(url, condition);
            if (result != null && result.Any())
            {
                // json setting 
                JsonSerializerSettings setting = new JsonSerializerSettings();
                setting.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;

                var t = (JArray)JsonConvert.DeserializeObject(result.First().Value, setting);
                return t;
            }
            return null;
        }

        #endregion **  Get with condition **

        #region ** Update **
        /// <summary>
        /// Update the entity by Code
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entity">the entity will be updated</param>
        /// <param name="objectLevel">the level object will be serialize</param>
        /// <returns>true if it's updated successfully, else it will be false</returns>
        public bool UpdateByCode<T>(IWinbooksOM entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity should not be null");

            if (!(entity is BaseOM))
                throw new Exception("entity is not Winbooks OM");

            PropertyInfo info = entity.GetType().GetProperty("Code");
            if (info == null)
                throw new ArgumentNullException("entity doesn't have the code property");

            object objCode = info.GetValue(entity);
            if (objCode == null)
                throw new ArgumentNullException("entity code should not be null");

            string entityCode = objCode.ToString();
            if (String.IsNullOrWhiteSpace(entityCode))
                throw new ArgumentNullException("Code cannot be empty");

            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "/", entityCode, "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = string.Empty;
                if (this.Setting.IsValidate)
                {
                    if (ValidateClass.ValidateWbObject((BaseOM)entity))
                    {
                        result = _userCredential.UpdateAsync(url,
                            WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, this.Setting.IsShowId, this.Setting.SendEntityLevel));
                        return true;
                    }
                    return false;
                }
                else
                {
                    result = _userCredential.UpdateAsync(url,
                        WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, this.Setting.IsShowId, this.Setting.SendEntityLevel));
                }

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool UpdateByCode<T>(IWinbooksOM entity, int entityLevel)
        {
            var holdSetting = this.Setting;
            this.Setting.SendEntityLevel = entityLevel;
            var result = UpdateByCode<T>(entity);
            this.Setting = holdSetting;
            return result;
        }

        public bool UpdateByCode<T>(IWinbooksOM entity, int entityLevel, bool isValidated)
        {
            var holdSetting = this.Setting;
            this.Setting.SendEntityLevel = entityLevel;
            this.Setting.IsValidate = isValidated;
            var result = UpdateByCode<T>(entity);
            this.Setting = holdSetting;
            return result;
        }

        public bool UpdateByCode<T>(string jsonString, string entityCode)
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "/", entityCode, "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter);
            _userCredential.UpdateAsync(url, jsonString);
            return true;
        }
        /// <summary>
        /// Update the entity by Id
        /// </summary>
        /// <typeparam name="T">the type of the entity : Customer</typeparam>
        /// <param name="entity">the entity will be updated</param>
        /// <returns>true if it's updated successfully, else it will be false</returns>
        public bool UpdateById<T>(IWinbooksOM entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity should not be null");

            PropertyInfo info = entity.GetType().GetProperty("Id");
            if (info == null)
                throw new ArgumentNullException("entity doesn't have the Id property");

            object objId = info.GetValue(entity);
            if (objId == null)
                throw new ArgumentNullException("entity code should not be null");

            string entityId = objId.ToString();
            if (String.IsNullOrWhiteSpace(entityId))
                throw new ArgumentNullException("Code cannot be empty");

            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "/", entityId, "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = string.Empty;
                if (this.Setting.IsValidate)
                {
                    if (ValidateClass.ValidateWbObject((BaseOM)entity))
                    {
                        string json = WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, this.Setting.IsShowId, this.Setting.SendEntityLevel);
                        result = _userCredential.UpdateAsync(url, json);
                        return true;
                    }
                    return false;
                }
                else
                {
                    string json = WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, this.Setting.IsShowId, this.Setting.SendEntityLevel);
                    result = _userCredential.UpdateAsync(url, json);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool UpdateById<T>(IWinbooksOM entity, int entityLevel)
        {
            var holdSetting = this.Setting;
            this.Setting.SendEntityLevel = entityLevel;
            var result = UpdateById<T>(entity);
            this.Setting = holdSetting;
            return result;
        }

        public bool UpdateById<T>(string jsonString, string entityId)
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "/", entityId, "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter);
            _userCredential.UpdateAsync(url, jsonString);
            return true;
        }

        /// <summary>
        /// Update a colllection of the data
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entities">the entities will be updated</param>
        /// <returns>true if it's updated successfully, else it will be false</returns>

        public bool UpdateCollection<T>(IList<T> entities) where T : Winbooks.TORM.OM.BaseOM
        {
            if (entities == null)
                throw new ArgumentNullException("entity should not be null");

            if (entities.Count == 0)
                throw new Exception("nothing to update");

            string entitiesData = WinbooksConverter.FromListWinbooksOBjectToJson(entities, this.Setting.IsShowId, this.Setting.SendEntityLevel);
            if (String.IsNullOrWhiteSpace(entitiesData))
                throw new Exception("data is empty");

            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = string.Empty;
                if (this.Setting.IsValidate)
                {
                    if (ValidateClass.ValidateListWbObject(entities))
                    {
                        result = _userCredential.UpdateAsync(url, entitiesData);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                result = _userCredential.UpdateAsync(url, entitiesData);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool UpdateCollection<T>(IList<T> entities, int entityLevel) where T : Winbooks.TORM.OM.BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.SendEntityLevel = entityLevel;
            var result = this.UpdateCollection(entities);
            this.Setting = holdSetting;
            return result;
        }

        public bool UpdateCollection<T>(string jsonLstString)
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter);
            _userCredential.UpdateAsync(url, jsonLstString);
            return true;
        }

        #endregion ** Update **

        #region ** Insert **
        /// <summary>
        /// Insert a entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entity">the entity will be inserted</param>
        /// <returns>true if it's inserted successfully, else it will be false</returns>
        public T Insert<T>(T entity) where T : BaseOM
        {
            if (entity == null)
                throw new ArgumentNullException("entity should not be null");

            if (!(entity is BaseOM))
                throw new Exception("entity is not Winbooks OM");
            string entityCode = "";

            PropertyInfo info = entity.GetType().GetProperty("Code");
            if (info != null)
            {
                object objCode = info.GetValue(entity);
                if (objCode == null)
                    throw new ArgumentNullException("entity code should not be null");

                entityCode = objCode.ToString();
                if (String.IsNullOrWhiteSpace(entityCode))
                    throw new ArgumentNullException("Code cannot be empty");
            }
            try
            {
                string entityName = typeof(T).Name;
                string parameter;
                if (entity is IHasCode)
                {
                    parameter = String.IsNullOrWhiteSpace(entityCode)
                        ? String.Concat("/", entityName, "/Folder/", this._folderCode)
                        : String.Concat("/", entityName, "/", entityCode, "/Folder/", this._folderCode);
                }
                else
                {
                    var entityWithId = entity as IHasId;
                    if (entityWithId != null)
                    {
                        parameter = entityWithId.Id == Guid.Empty
                            ? String.Concat("/", entityName, "/Folder/", this._folderCode)
                            : String.Concat("/", entityName, "/", entityWithId.Id, "/Folder/", this._folderCode);
                    }
                    else
                    {
                        parameter = String.Concat("/", entityName, "/Folder/", this._folderCode);
                    }
                }
                string url = _userCredential.GetUrl(parameter);
                string result = "";
                if (this.Setting.IsValidate)
                {
                    if (ValidateClass.ValidateWbObject((BaseOM)entity))
                    {
                        string json = WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, Setting.IsShowId, this.Setting.SendEntityLevel);
                        result = _userCredential.InsertAsync(url, json);

                        if (this.Setting.IsReUpdateData)
                        {
                            if (!string.IsNullOrEmpty(result))
                            {
                                // get href value 
                                var resultJObj = JObject.Parse(result);
                                if (resultJObj != null && resultJObj["Href"] != null)
                                {
                                    // string return url 
                                    var returnUrl = resultJObj["Href"].Value<string>();
                                    // add filter level
                                    returnUrl = _userCredential.GetUrlWithSettingFilter(returnUrl, this.Setting);
                                    entity = GetByInputUrl<T>(returnUrl);
                                    return entity;
                                }
                            }
                        }
                        if (entity.Id == Guid.Empty)
                        {
                            Guid id = ExtractGuid(entityName, result);
                            entity.Id = id;
                        }
                        return entity;
                    }
                    return null;
                }
                else
                {
                    string json = WinbooksConverter.FromWinbooksObjectToJson((BaseOM)entity, this.Setting.IsShowId, this.Setting.SendEntityLevel);
                    result = _userCredential.InsertAsync(url, json);
                    if (this.Setting.IsReUpdateData)
                    {
                        if (!string.IsNullOrEmpty(result))
                        {
                            // get href value 
                            var resultJObj = JObject.Parse(result);
                            if (resultJObj != null && resultJObj["Href"] != null)
                            {
                                // string return url 
                                var returnUrl = resultJObj["Href"].Value<string>();
                                // add filter level
                                returnUrl = _userCredential.GetUrlWithSettingFilter(returnUrl, this.Setting);
                                entity = GetByInputUrl<T>(returnUrl);
                                return entity;
                            }
                        }
                    }
                }
                return entity;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private Guid ExtractGuid(string entityName, string result)
        {
            Guid id = Guid.Empty;
            if (string.IsNullOrEmpty(entityName))
                return Guid.Empty;


            // result = "{\"Href\":\"http://stg.winbooksapis.be:8081/app/GLTransHeader/dc61eed4-67aa-41c4-bcbb-aafa00f2b068/Folder/JF_TEST_RESTAPI\"}"
            int pos = result.IndexOf("/" + entityName + "/");
            if (pos < 0)
                return Guid.Empty;

            //           0123456789012345678901234567
            // result = "0123/ALTrans/guid/Folder/..."
            pos = pos + entityName.Length + 2;  // 4 + 7 + 2 = 13
            string guid = result.Substring(pos);
            pos = guid.IndexOf("/Folder/");
            if (pos != 36)
                return Guid.Empty;
            guid = guid.Substring(0, 36);
            if (!Guid.TryParse(guid, out id))
                return Guid.Empty;
            return id;
        }

        public bool Insert<T>(T entity, bool isValidate, bool isReUpdate) where T : BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.IsValidate = isValidate;
            this.Setting.IsReUpdateData = isReUpdate;
            var result = Insert<T>(entity);
            this.Setting = holdSetting;
            return result != null;
        }
        /// <summary>
        /// Insert a entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entity">the entity will be inserted</param>
        /// <param name="entityLevel"> the level entity will be serialized</param>
        /// <returns></returns>
        public T Insert<T>(T entity, int entityLevel) where T : BaseOM
        {
            var holdLevel = this.Setting.SendEntityLevel;
            this.Setting.SendEntityLevel = entityLevel;
            var result = Insert<T>(entity);
            this.Setting.SendEntityLevel = holdLevel;
            return result;
        }

        /// <summary>
        /// Insert a entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entity">the entity will be inserted</param>
        /// <param name="isValidate">indicate data should be validate or not before sent to sever</param>
        /// <returns>true if it's inserted successfully, else it will be false</returns>
        public T Insert<T>(T entity, bool isValidate) where T : BaseOM
        {
            this.Setting.IsValidate = isValidate;
            return Insert<T>(entity);
        }

        public T Insert<T>(T entity, int entityLevel, bool isValidate) where T : BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.IsValidate = isValidate;
            this.Setting.SendEntityLevel = entityLevel;
            var result = Insert<T>(entity);
            this.Setting = holdSetting;
            return result;
        }

        public T Insert<T>(T entity, int entityLevel, bool isValidate, bool isShowId) where T : BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.IsValidate = isValidate;
            this.Setting.SendEntityLevel = entityLevel;
            this.Setting.IsShowId = isShowId;
            var result = Insert<T>(entity);
            this.Setting = holdSetting;
            return result;
        }

        public bool Insert<T>(string jsonString, string codeOrId)
        {
            string entityName = typeof(T).Name;
            string parameter;
            parameter = String.IsNullOrWhiteSpace(codeOrId)
                    ? String.Concat("/", entityName, "/Folder/", this._folderCode)
                    : String.Concat("/", entityName, "/", codeOrId, "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter);
            var result = _userCredential.InsertAsync(url, jsonString);
            return true;
        }
        /// <summary>
        /// Insert the collection of the entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entities">the entities will be inserted</param>
        /// <returns>true if it's inserted successfully, else it will be false</returns>
        public bool InsertCollection<T>(IList<T> entities) where T : Winbooks.TORM.OM.BaseOM
        {
            if (entities == null)
                throw new ArgumentNullException("entities should not be null");

            if (entities.Count == 0)
                throw new Exception("nothing to insert");

            string entitiesData = WinbooksConverter.FromListWinbooksOBjectToJson(entities, this.Setting.IsShowId, this.Setting.SendEntityLevel);
            if (String.IsNullOrWhiteSpace(entitiesData))
                throw new Exception("data is empty");

            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = _userCredential.InsertAsync(url, entitiesData);
                if (this.Setting.IsReUpdateData && !string.IsNullOrEmpty(result))
                {
                    var resutlJObject = JObject.Parse(result);
                    if (resutlJObject != null)
                    {
                        // get NumberOfSuccess 
                        int NumberOfSuccess = resutlJObject["NumberOfSuccess"].Value<int>();
                        if (NumberOfSuccess == entities.Count)
                        {
                            // get lst UrlSucess 
                            var lstUrl = resutlJObject["ListUrlSuccess"].Values<string>();
                            var lstReturn = new List<T>();
                            foreach (string urlString in lstUrl)
                            {
                                // get url with filter
                                string returnUrl = _userCredential.GetUrlWithSettingFilter(urlString, this.Setting);
                                lstReturn.Add(GetByInputUrl<T>(returnUrl));
                            }
                            entities = lstReturn;
                        }
                        else
                        {
                            throw new Exception("Result insert data NumberOfSuccess not is not consistent with number input data");
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Insert the collection of the entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entities">the entities will be inserted</param>
        /// <param name="entityLevel">the level object will be serialize</param>
        /// <returns></returns>
        public bool InsertCollection<T>(IList<T> entities, int entityLevel) where T : Winbooks.TORM.OM.BaseOM
        {
            var holdLevel = this.Setting.SendEntityLevel;
            this.Setting.SendEntityLevel = entityLevel;
            var result = InsertCollection(entities);
            this.Setting.SendEntityLevel = holdLevel;
            return result;
        }

        /// <summary>
        /// Insert the collection of the entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entities">the entities will be inserted</param>
        /// <param name="entityLevel">the level object will be serialize</param>
        /// <param name="isValidate">data should be validate or not</param>
        /// <returns></returns>
        public bool InsertCollection<T>(IList<T> entities, int entityLevel, bool isValidate) where T : Winbooks.TORM.OM.BaseOM
        {
            var holdSetting = this.Setting;
            this.Setting.SendEntityLevel = entityLevel;
            this.Setting.IsValidate = isValidate;
            var result = InsertCollection(entities);
            this.Setting = holdSetting;
            return result;
        }

        /// <summary>
        /// Insert the collection of the entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entities">the entities will be inserted</param>
        /// <param name="isValidate">data should be validate or not</param>
        /// <returns></returns>
        public bool InsertCollection<T>(IList<T> entities, bool isValidate) where T : Winbooks.TORM.OM.BaseOM
        {
            var holdIsValidate = this.Setting.IsValidate;
            this.Setting.IsValidate = isValidate;
            var result = InsertCollection(entities);
            this.Setting.IsValidate = holdIsValidate;
            return result;
        }

        public bool InsertCollection<T>(string lstJsonEntities)
        {
            string entityName = typeof(T).Name;
            string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
            string url = _userCredential.GetUrl(parameter);
            string result = _userCredential.InsertAsync(url, lstJsonEntities);
            return true;
        }

        #endregion ** Insert **

        #region ** Delete **
        /// <summary>
        /// Delete by code
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entityCode">the code the entity will be deleted</param>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteByCode<T>(string entityCode)
        {
            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "/", entityCode, "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = _userCredential.DeleteAsync(url);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Delete entity by id
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entityId">the id of the entity will be deleted</param>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteById<T>(Guid entityId)
        {
            return DeleteById<T>(entityId.ToString());
        }

        /// <summary>
        /// Delete entity by id
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <param name="entityId">the id of the entity will be deleted</param>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteById<T>(string entityId)
        {
            try
            {
                string entityName = typeof(T).Name;

                string parameter = String.Concat("/", entityName, "/", entityId, "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = _userCredential.DeleteAsync(url);

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Delete the entities with filter
        /// </summary>
        /// <typeparam name="T">the type of the entities</typeparam>
        /// <param name="condition">the filter to delete</param>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteFilter<T>(Winbooks.TORM.DAL.ICriteria criteria)
        {
            string condition = (criteria == null) ? "" : criteria.JsonCriteriaSerialize();
            return DeleteFilter<T>(condition);
        }

        /// <summary>
        /// Delete the entities with the filter
        /// </summary>
        /// <typeparam name="T">the type of the entities</typeparam>
        /// <param name="condition">the filter to delete</param>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteFilter<T>(string condition)
        {
            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = _userCredential.DeleteAsync(url, condition);

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Delete all the entity
        /// </summary>
        /// <typeparam name="T">the type of the entity</typeparam>
        /// <returns>true if it's deleted successfully, else it will be false</returns>
        public bool DeleteAll<T>()
        {
            try
            {
                string entityName = typeof(T).Name;
                string parameter = String.Concat("/", entityName, "s", "/Folder/", this._folderCode);
                string url = _userCredential.GetUrl(parameter);
                string result = _userCredential.DeleteAsync(url);

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion ** Delete **

        #region ** Checking Access **

        public bool CheckingAccess()
        {
            try
            {
                if (this._userCredential != null)
                {
                    return this._userCredential.Authentication();
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion
    }
}
