using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Sockeye.Util;
using Sockeye.Api.ControllerHelpers;
using Sockeye.Models;
using Sockeye.DataList;


namespace Sockeye.Biz
{


    internal class DataListSavedFilterBiz : BizObject
    {

        internal DataListSavedFilterBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
        {
            ct = dbcontext;
            UserId = currentUserId;
            UserTranslationId = userTranslationId;
            CurrentUserRoles = UserRoles;
            BizType = SockType.DataListSavedFilter;
        }

        internal static DataListSavedFilterBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
        {
            if (httpContext != null)
                return new DataListSavedFilterBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
            else
                return new DataListSavedFilterBiz(ct, 1, ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin);
        }



        ////////////////////////////////////////////////////////////////////////////////////////////////
        //EXISTS 
        internal async Task<bool> ExistsAsync(long id)
        {
            return await ct.DataListSavedFilter.AnyAsync(z => z.Id == id);
        }



        ////////////////////////////////////////////////////////////////////////////////////////////////
        //CREATE        
        internal async Task<DataListSavedFilter> CreateAsync(DataListSavedFilter inObj)
        {
            Validate(inObj, true);
            if (HasErrors)
                return null;
            else
            {
                //do stuff with datafilter
                DataListSavedFilter outObj = inObj;
                outObj.UserId = UserId;
                await ct.DataListSavedFilter.AddAsync(outObj);
                await ct.SaveChangesAsync();
                return outObj;

            }
        }




        ////////////////////////////////////////////////////////////////////////////////////////////////
        /// GET

        //Get one
        internal async Task<DataListSavedFilter> GetAsync(long fetchId)
        {
            //This is simple so nothing more here, but often will be copying to a different output object or some other ops           
            var ret = await ct.DataListSavedFilter.AsNoTracking().SingleOrDefaultAsync(z => z.Id == fetchId && (z.Public == true || z.UserId == UserId));

            return ret;
        }




        //GET FILTERLIST 
        internal async Task<List<NameIdDefaultItem>> GetViewListAsync(string listKey)
        {
            await EnsureDefaultAsync(listKey);
            return await ct.DataListSavedFilter
                             .AsNoTracking()
                             .Where(z => z.ListKey == listKey && (z.Public == true || z.UserId == UserId))
                             .OrderBy(z => z.Name)
                              .Select(z => new NameIdDefaultItem(z.Id, z.Name, z.DefaultFilter)).ToListAsync();
        }


        ////////////////////////////////////////////////////////////////////////////////////////////////
        //CREATE Default if it doesn't exist already        
        internal async Task EnsureDefaultAsync(string listKey)
        {
            if (!await ct.DataListSavedFilter.AnyAsync(z => z.UserId == UserId && z.ListKey == listKey && z.DefaultFilter == true))
            {
                if (!DataListFactory.ListKeyIsValid(listKey))
                {
                    throw new System.ArgumentOutOfRangeException($"ListKey '{listKey}' is not a valid DataListKey");
                }

                //var dataList = DataListFactory.GetAyaDataList(listKey,0);

                DataListSavedFilter d = new DataListSavedFilter();
                d.ListKey = listKey;
                d.Name = "-";
                d.DefaultFilter = true;
                d.Public = false;
                d.UserId = UserId;
                d.Filter = "[]";//empty array is default becuase it would be filter:[{column:"PartName",any:true/false,items:[{op: "=",value: "400735"}]}]
                await ct.DataListSavedFilter.AddAsync(d);
                await ct.SaveChangesAsync();
            }
        }


        ////////////////////////////////////////////////////////////////////////////////////////////////
        //UPDATE
        //

        //put
        internal async Task<DataListSavedFilter> PutAsync(DataListSavedFilter putObject)
        {
            var dbObject = await GetAsync(putObject.Id);
            if (dbObject == null)
            {
                AddError(ApiErrorCode.NOT_FOUND, "id");
                return null;
            }
            if (dbObject.Concurrency != putObject.Concurrency)
            {
                AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
                return null;
            }

            //preserve the owner ID if none was specified
            if (putObject.UserId == 0)
                putObject.UserId = dbObject.UserId;

            Validate(dbObject, false);
            if (HasErrors)
                return null;

            ct.Replace(dbObject, putObject);
               try
            {
                await ct.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!await ExistsAsync(putObject.Id))
                    AddError(ApiErrorCode.NOT_FOUND);
                else
                    AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
                return null;
            }

            return putObject;
        }


        ////////////////////////////////////////////////////////////////////////////////////////////////
        //DELETE
        //
        internal async Task<bool> DeleteAsync(DataListSavedFilter dbObject)
        {
            ValidateCanDelete(dbObject);
            if (HasErrors)
                return false;
            ct.DataListSavedFilter.Remove(dbObject);
            await ct.SaveChangesAsync();

            //was it a "reset" of default?
            if (dbObject.DefaultFilter)
                await EnsureDefaultAsync(dbObject.ListKey);
            return true;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////
        //VALIDATION
        //

        private void ValidateCanDelete(DataListSavedFilter inObj)
        {
            if (inObj.UserId != UserId)
            {
                AddError(ApiErrorCode.NOT_AUTHORIZED, "generalerror", "Can't delete another users filter");
            }
        }


        //Can save or update?
        private void Validate(DataListSavedFilter inObj, bool isNew)
        {

            //UserId required
            if (!isNew)
            {
                if (inObj.UserId == 0)
                    AddError(ApiErrorCode.VALIDATION_REQUIRED, "UserId");
            }

            //can't save a filter for someone else
            if (inObj.UserId != UserId && inObj.Public == false)
            {
                AddError(ApiErrorCode.NOT_AUTHORIZED, "generalerror", "Can only save private filters for self not other UserId's");
                return;
            }

            //technically not "validation" but always ensure default filter has - name
            if (inObj.DefaultFilter)
            {
                inObj.Name = "-";
            }

            //Name required
            if (string.IsNullOrWhiteSpace(inObj.Name))
                AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name");

            if (string.IsNullOrWhiteSpace(inObj.Filter))
                AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter");

            if (string.IsNullOrWhiteSpace(inObj.ListKey))
                AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListKey");


            if (!DataListFactory.ListKeyIsValid(inObj.ListKey))
            {
                AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid");
            }

            if (inObj.ListKey.Length > 255)
                AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "ListKey", "255 max");

            //check if filter can be reconstructed into a C# filter object
            try
            {
                JsonConvert.DeserializeObject<List<DataListFilterOption>>(inObj.Filter);
            }
            catch (System.Exception ex)
            {
                AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", "Filter is not valid JSON string, can't convert to List<DataListFilterOption>, error: " + ex.Message);
            }

            return;
        }



        /////////////////////////////////////////////////////////////////////

    }//eoc


}//eons

