using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;

namespace AyaNova.Biz
{

    internal class QuoteBiz : BizObject, ISearchAbleObject
    {
        //Feature specific roles
        internal static AuthorizationRoles RolesAllowedToChangeSerial = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.AccountingFull;

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

        internal static QuoteBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
        {

            if (httpContext != null)
                return new QuoteBiz(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 QuoteBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
        }



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

        //###################################################################################################################################################
        //###################################################################################################################################################
        // WARNING! THIS OBJECT IS AN INITIAL TEST VERSION NOT UP TO CURRENT STANDARDS, SEE WORKORDERBIZ FOR HOW THIS SHOULD BE CODED
        //###################################################################################################################################################
        //###################################################################################################################################################


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

        internal async Task<Quote> GetAsync(long fetchId, bool logTheGetEvent = true)
        {
            //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.Quote.SingleOrDefaultAsync(z => z.Id == fetchId);
            if (logTheGetEvent && ret != null)
            {
                //Log
                await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
            }
            return ret;
        }


        //###################################################################################################################################################
        //###################################################################################################################################################
        // WARNING! THIS OBJECT IS AN INITIAL TEST VERSION NOT UP TO CURRENT STANDARDS, SEE WORKORDERBIZ FOR HOW THIS SHOULD BE CODED
        //###################################################################################################################################################
        //###################################################################################################################################################


        ////////////////////////////////////////////////////////////////////////////////////////////////
        //CREATE   

        //Called from route and also seeder
        internal async Task<Quote> CreateAsync(long? workorderTemplateId, long? customerId, uint? serial)
        {
            //Create and save to db a new workorder and return it
            //NOTE: Serial can be specified or edited after the fact in a limited way by full role specfic only!! (service manager, bizadminfull, accounting maybe)

            if (serial != null && !Authorized.HasAnyRole(CurrentUserRoles, RolesAllowedToChangeSerial))
            {
                AddError(ApiErrorCode.NOT_AUTHORIZED, "Serial");
                return null;
            }


            //  await ValidateAsync(inObj, null);
            // if (HasErrors)
            //     return null;
            // else
            // {
            //do stuff with Quote
            Quote o = new Quote();

            //TODO: template
            //TODO: CUSTOMER ID


            //Save to db                              
            await ct.Quote.AddAsync(o);
            await ct.SaveChangesAsync();
            //Handle child and associated items:                
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, o.Id, BizType, AyaEvent.Created), ct);
            await SearchIndexAsync(o, true);
            // await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, o.Tags, null);

            return o;
            //}
        }




        ////////////////////////////////////////////////////////////////////////////////////////////////
        //DUPLICATE
        //

        internal async Task<Quote> DuplicateAsync(Quote dbObject)
        {
            await Task.CompletedTask;
            throw new System.NotImplementedException("STUB: WORKORDER DUPLICATE");
            // Quote outObj = new Quote();
            // CopyObject.Copy(dbObject, outObj, "Wiki");
            // // outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
            // //generate unique name
            // string newUniqueName = string.Empty;
            // bool NotUnique = true;
            // long l = 1;
            // do
            // {
            //     newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObject.Name, l++, 255);
            //     NotUnique = await ct.Quote.AnyAsync(z => z.Name == newUniqueName);
            // } while (NotUnique);

            // outObj.Name = newUniqueName;


            // outObj.Id = 0;
            // outObj.Concurrency = 0;

            // await ct.Quote.AddAsync(outObj);
            // await ct.SaveChangesAsync();

            // //Handle child and associated items:                
            // await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
            // await SearchIndexAsync(outObj, true);
            // await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
            // return outObj;

        }

        //###################################################################################################################################################
        //###################################################################################################################################################
        // WARNING! THIS OBJECT IS AN INITIAL TEST VERSION NOT UP TO CURRENT STANDARDS, SEE WORKORDERBIZ FOR HOW THIS SHOULD BE CODED
        //###################################################################################################################################################
        //###################################################################################################################################################


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

        //put
        internal async Task<bool> PutAsync(Quote dbObject, Quote putObj)
        {

            // make a snapshot of the original for validation but update the original to preserve workflow           
            Quote SnapshotOfOriginalDBObj = new Quote();
            CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj);

            //Replace the db object with the PUT object
            CopyObject.Copy(putObj, dbObject, "Id,Serial");

            //if user has rights then change it, otherwise just ignore it and do the rest
            if (SnapshotOfOriginalDBObj.Serial != putObj.Serial && Authorized.HasAnyRole(CurrentUserRoles, RolesAllowedToChangeSerial))
            {
                dbObject.Serial = putObj.Serial;
            }

            dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags);
            dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields);

            //Set "original" value of concurrency token to input token
            //this will allow EF to check it out
            ct.Entry(dbObject).OriginalValues["Concurrency"] = putObj.Concurrency;


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

            //Log event and save context          
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
            await SearchIndexAsync(dbObject, false);
            await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags);

            return true;
        }



        private async Task SearchIndexAsync(Quote obj, bool isNew)
        {
            //SEARCH INDEXING
            var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
            SearchParams.AddText(obj.Notes).AddText(obj.Serial).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);

            if (isNew)
                await Search.ProcessNewObjectKeywordsAsync(SearchParams);
            else
                await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams);
        }

        public async Task<Search.SearchIndexProcessObjectParameters> GetSearchResultSummary(long id)
        {
            var obj = await ct.Quote.SingleOrDefaultAsync(z => z.Id == id);
            var SearchParams = new Search.SearchIndexProcessObjectParameters();
            if (obj != null)
                SearchParams.AddText(obj.Notes).AddText(obj.Serial).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
            return SearchParams;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////
        //DELETE
        //
        internal async Task<bool> DeleteAsync(Quote dbObject)
        {
            await Task.CompletedTask;
            throw new System.NotImplementedException("STUB: WORKORDER DELETE");
            //Determine if the object can be deleted, do the deletion tentatively
            //Probably also in here deal with tags and associated search text etc

            //NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObject);
            // if (HasErrors)
            //     return false;
            // ct.Quote.Remove(dbObject);
            // await ct.SaveChangesAsync();

            // //Log event 
            // await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Serial.ToString(), ct);
            // await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType);
            // await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
            // return true;
        }



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

        //Can save or update?
        private async Task ValidateAsync(Quote proposedObj, Quote currentObj)
        {

            //run validation and biz rules
            bool isNew = currentObj == null;

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

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

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


            //Any form customizations to validate?
            var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.Quote.ToString());
            if (FormCustomization != null)
            {
                //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required

                //validate users choices for required non custom fields
                RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);

                //validate custom fields
                CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields);
            }

        }


        //Can delete?
        // private void ValidateCanDelete(Quote inObj)
        // {
        //     //whatever needs to be check to delete this object
        // }



        ////////////////////////////////////////////////////////////////////////////////////////////////
        //JOB / OPERATIONS
        //


        //Other job handlers here...


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

    }//eoc


}//eons

