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


namespace AyaNova.Biz
{


    internal class FormCustomBiz : BizObject
    {

        internal FormCustomBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
        {
            ct = dbcontext;
            UserId = currentUserId;
            UserTranslationId = userTranslationId;
            CurrentUserRoles = UserRoles;
            BizType = AyaType.FormCustom;
        }

        internal static FormCustomBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
        {
            if (httpContext != null)
                return new FormCustomBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
            else//when called internally for internal ops there will be no context so need to set default values for that
                return new FormCustomBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
        }

      

        ////////////////////////////////////////////////////////////////////////////////////////////////
        //EXISTS 
        internal async Task<bool> ExistsAsync(string formKey)
        {
            return await ct.FormCustom.AnyAsync(z => z.FormKey == formKey);
        }



        ////////////////////////////////////////////////////////////////////////////////////////////////
        //CREATE        
        internal async Task<FormCustom> CreateAsync(FormCustom inObj)
        {
            await ValidateAsync(inObj, true);
            if (HasErrors)
                return null;
            else
            {
                //
                FormCustom outObj = inObj;

                outObj.Template = JsonUtil.CompactJson(outObj.Template);

                await ct.FormCustom.AddAsync(outObj);
                await ct.SaveChangesAsync();

                //Handle child and associated items:

                //EVENT LOG
                await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);

                
                return outObj;

            }
        }



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

        //Get one
        internal async Task<FormCustom> GetAsync(string formKey)
        {
            //Step 1: check if exists, if it does then just return it
            if (await ExistsAsync(formKey))
            {
                return await ct.FormCustom.SingleOrDefaultAsync(z => z.FormKey == formKey);
            }

            //If it doesn't exist, vet the form key name is ok by checking with this list            
            if (!AyaFormFieldDefinitions.AyaFormFieldDefinitionKeys.Contains(formKey))
            {
                //Nope, whatever it is, it's not valid
                return null;
            }

            // Name is valid, make a new formcustom for the name specified, save to db and then return it
            //NOTE: This assumes that the client will make it a legal form custom and so it doesn't include any pre-defined stock fields that are required etc
            //this just makes an empty template suitable for the client to work with
            var fc = new FormCustom()
            {
                FormKey = formKey,
                Template = @"[]"
            };

            //Create and save to db
            return await CreateAsync(fc);

        }





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

        //put
        internal async Task<bool> PutAsync(FormCustom dbObject, FormCustom inObj)
        {

            //Replace the db object with the PUT object
            CopyObject.Copy(inObj, dbObject, "Id");
            //Set "original" value of concurrency token to input token
            //this will allow EF to check it out
            ct.Entry(dbObject).OriginalValues["Concurrency"] = inObj.Concurrency;

            await ValidateAsync(dbObject, false);
            if (HasErrors)
                return false;

            dbObject.Template = JsonUtil.CompactJson(dbObject.Template);
            await ct.SaveChangesAsync();

            //Log modification and save context
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);

            
            return true;
        }


        //NO DELETE, ONLY EDIT

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

        //Can save or update?
        private async Task ValidateAsync(FormCustom inObj, bool isNew)
        {

            //FormKey required and must be valid
            if (string.IsNullOrWhiteSpace(inObj.FormKey))
                AddError(ApiErrorCode.VALIDATION_REQUIRED, "FormKey");
            else
            {
                if (!AyaFormFieldDefinitions.IsValidFormFieldDefinitionKey(inObj.FormKey))
                {
                    AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "FormKey");
                }
            }

            //FormKey must be less than 255 characters
            if (inObj.FormKey.Length > 255)
                AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "FormKey", "255 max");

            //If name is otherwise OK, check that name is unique
            if (!PropertyHasErrors("FormKey") && isNew)
            {
                //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
                if (await ct.FormCustom.AnyAsync(z => z.FormKey == inObj.FormKey && z.Id != inObj.Id))
                {
                    AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "FormKey");
                }
            }




            //Template json must parse
            if ((!PropertyHasErrors("FormKey") && !string.IsNullOrWhiteSpace(inObj.Template)))
            {
                var ValidCustomFieldTypes = CustomFieldType.ValidCustomFieldTypes;
                var ValidFormFields = AyaFormFieldDefinitions.AyaFormFields(inObj.FormKey);
                try
                {
                    //Parse the json, expecting something like this:
                    //[{fld:"ltkeyfieldname",hide:"true/false",required:"true/false", type:"bool"},{fld:"ltkeyfieldname",hide:"true/false",required:"true/false", type:"text"]
                    //Array at root is valid json and saves a bit of bandwidth so minimal is best

                    //Only Custom fields that are to be displayed need to be in this fragment.
                    //fields that are not chosen to display don't need to be in the fragment to be valid

                    var v = JArray.Parse(inObj.Template);

                    for (int i = 0; i < v.Count; i++)
                    {
                        AyaFormFieldDefinition MasterFormField = null;

                        var formFieldItem = v[i];
                        if (formFieldItem["fld"] == null)
                            AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template", $"Template array item {i}, object is missing required \"fld\" property ");
                        else
                        {
                            var fldKey = formFieldItem["fld"].Value<string>();
                            if (string.IsNullOrWhiteSpace(fldKey))
                                AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template", $"Template array item {i}, \"fld\" property exists but is empty, a value is required");

                            //validate the field name if we can
                            if (ValidFormFields != null)
                            {

                                if (!ValidFormFields.Exists(z => z.FieldKey == fldKey))
                                {
                                    AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i}, fld property value \"{fldKey}\" is not a valid form field value for formKey specified");
                                }
                                else
                                {
                                    MasterFormField = ValidFormFields.FirstOrDefault(z => z.FieldKey == fldKey);
                                }

                            }
                        }


                        if (MasterFormField != null)
                        {

                            if (formFieldItem["hide"] != null)
                            {
                                var fieldHideValue = formFieldItem["hide"].Value<bool>();
                                if (!MasterFormField.Hideable && fieldHideValue == true)
                                    AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i} (\"{MasterFormField.FieldKey}\"), \"hide\" property value of \"{fieldHideValue}\" is not valid, this field is core and cannot be hidden");
                            }

                            //validate if it's a custom field that it has a type specified
                            if (MasterFormField.IsCustomField && formFieldItem["type"] == null)
                            {
                                AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i} (\"{MasterFormField.FieldKey}\"), \"type\" property value is MISSING for custom field, Custom fields MUST have types specified");
                            }



                            if (formFieldItem["type"] != null)
                            {
                                if (!MasterFormField.IsCustomField)
                                    AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i} (\"{MasterFormField.FieldKey}\"), \"type\" property value is not valid, only Custom fields can have types specified");
                                else
                                {//It is a custom field, is it a valid type value
                                    var templateFieldCustomTypeValue = formFieldItem["type"].Value<int>();
                                    if (!ValidCustomFieldTypes.Contains(templateFieldCustomTypeValue))
                                        AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i} (\"{MasterFormField.FieldKey}\"), \"type\" property value of \"{templateFieldCustomTypeValue}\" is not a valid custom field type");
                                }
                            }

                        }

                        //other code depends on seeing the required value even if it's not set to true
                        if (formFieldItem["required"] == null)
                            AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template", $"Template array item {i}, object is missing \"required\" property.  All items must contain this property. ");


                        //NOTE: value of nothing, null or empty is a valid value so no checking for it here
                    }
                }
                catch (Newtonsoft.Json.JsonReaderException ex)
                {
                    AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", "Template is not valid JSON string: " + ex.Message);

                }
            }





            return;
        }


        // //Can delete?
        // private void ValidateCanDelete(FormCustom inObj)
        // {
        //     //Leaving this off for now
        // }





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

    }//eoc


}//eons

