using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sockeye.Models;
using Sockeye.Biz;
using Newtonsoft.Json.Linq;
using Microsoft.EntityFrameworkCore;

namespace Sockeye.DataList
{

    /// <summary>
    /// DataList object base class
    /// </summary>
    internal abstract class DataListProcessingBase : IDataListProcessing
    {
        //CoreBizObject add here
        //well, not here exactly but add a new DATALIST class if it will be displayed as a list anywhere in the UI or reported on
        public DataListProcessingBase()
        {
            /*
            NOTE: all sql identifiers need to be explicitly identified as understood by postgres

            DefaultColumns = new List<string>() { "XXX", "XXXX", "XXXX", "XXXX", "XXXX", "XXX", "XXXX", "XXXX", "XXXX", "XXXX" };
            DefaultSortBy = new Dictionary<string, string>() { { "XXXX", "+" }, { "XXXX", "-" } };
            */
        }

        public string SQLFrom { get; set; }
        public List<DataListFieldDefinition> FieldDefinitions { get; set; }
        public AuthorizationRoles AllowedRoles { get; set; }
        public SockType DefaultListAType { get; set; }
        //public long CurrentUserId { get; set; }
        //public long CurrentUserTranslationId { get; set; }
        public List<string> DefaultColumns { get; set; }
        public Dictionary<string, string> DefaultSortBy { get; set; }

        //set defaults if not provided in listOptions
        public void SetListOptionDefaultsIfNecessary(Models.DataListProcessingBase listOptions)
        {
            //columns, filter and sortby could all be null
            if (listOptions.Filter == null)
                listOptions.Filter = new List<DataListFilterOption>();

            if (listOptions.SortBy == null)
                listOptions.SortBy = new Dictionary<string, string>();

            //Check Columns
            if (listOptions is DataListTableProcessingOptions)
            {
                var dlto = ((DataListTableProcessingOptions)listOptions);
                if (dlto.Columns == null)
                    dlto.Columns = new List<string>();
                //if this doesn't work then just ditch this method in favor of local code, it's not really saving much 
                if (dlto.Columns.Count == 0)
                    dlto.Columns = DefaultColumns;
            }

            //Check SortBy
            if (listOptions.SortBy.Count == 0)
                listOptions.SortBy = DefaultSortBy;

            //Check filter
            if (listOptions.Filter == null)
            {

            }
        }

        public Newtonsoft.Json.Linq.JArray GenerateReturnListColumns(List<string> columns)
        {
            var CustomFieldDefinitions = GetCustomFieldDefinitionsForList();

            //Generate JSON fragment to return with column definitions
            StringBuilder sb = new StringBuilder();

            sb.Append("[");

            bool FirstColumnAdded = false;

            foreach (string s in columns)
            {
                DataListFieldDefinition o = FieldDefinitions.FirstOrDefault(z => z.FieldKey == s);
#if (DEBUG)
                //Developers little helper
                if (o == null)
                {
                    throw new System.ArgumentNullException($"DEV ERROR in AyaDataList::GenerateReturnListColumns - field {s} specified in columns was NOT found in ObjectFields list");
                }
#endif

                if (o != null)
                {//Here is where we can vet the field name, if it doesn't exist. For production we'll just ignore those ones

                    if (FirstColumnAdded)
                        sb.Append(",");
                    sb.Append("{");
                    //Build required part of column definition
                    if (!o.IsCustomField)
                        sb.Append($"\"cm\":\"{o.TKey}\",\"dt\":{(int)o.UiFieldDataType}");
                    else
                    {
                        //insert specific type for this custom field
                        if (CustomFieldDefinitions.ContainsKey(o.TKey))
                        {
                            var customFieldType = CustomFieldDefinitions[o.TKey];
                            sb.Append($"\"cm\":\"{o.TKey}\",\"dt\":{customFieldType}");
                        }
                        else
                        {
                            //this is normal as there may not be a definition for a Custom field but it's been requested so just treat it like text                            
                            sb.Append($"\"cm\":\"{o.TKey}\",\"dt\":{(int)UiFieldDataType.Text}");
                        }
                    }

                    //Has a AyAType? (linkable / openable)
                    if (o.SockType != 0)
                        sb.Append($",\"sock\":{(int)o.SockType}");

                    //Row ID column?
                    if (o.IsRowId)
                    {
                        sb.Append($",\"rid\":1");
                    }

                    //Has a Enumtype?
                    if (!string.IsNullOrEmpty(o.EnumType))
                        sb.Append($",\"et\":\"{Sockeye.Util.StringUtil.TrimTypeName(o.EnumType)}\"");


                    //field key needed for sorting etc
                    sb.Append($",\"fk\":\"{o.FieldKey}\"");

                    //Not Sortable? 
                    if (!o.IsSortable)
                        sb.Append($",\"ns\":1");

                    //Not Filterable?
                    if (!o.IsFilterable)
                        sb.Append($",\"nf\":1");

                    //translate required?
                    if (o.Translate)
                        sb.Append($",\"tra\":1");


                    sb.Append("}");
                    FirstColumnAdded = true;

                }
            }
            sb.Append("]");

            return JArray.Parse(sb.ToString());
        }


        //Find and return a dictionary of all custom fields definitions for all types in list
        //used to build the column array and define specific type defined for custom fields so client datatable 
        //knows how to format it
        private Dictionary<string, int> GetCustomFieldDefinitionsForList()
        {
            //all keys and types can go in the same list since they are unique to each type of list
            //i.e. both users and widget custom fields can be in the same list
            Dictionary<string, int> ret = new Dictionary<string, int>();
            List<string> typesProcessed = new List<string>();
            //custom fields handling
            foreach (DataListFieldDefinition d in this.FieldDefinitions)
            {
                if (d.IsCustomField)
                {
                    //this relies on the convention I'm using of SockType name as the first part of all custom fields lT keys, e.g.
                    //WidgetCustom1 -> Widget
                    var aysockTypename = d.TKey.Split("Custom")[0];
                    if (!typesProcessed.Contains(aysockTypename))
                    {
                        //make sure we do each type only once
                        typesProcessed.Add(aysockTypename);
                        //fetch it and set it
                        using (var ct = Sockeye.Util.ServiceProviderProvider.DBContext)
                        {
                            var fc = ct.FormCustom.AsNoTracking().SingleOrDefault(z => z.FormKey == aysockTypename);
                            //normal condition
                            if (fc == null)
                                continue;

                            //iterate the fields and add each custom one with a type to the return dictionary
                            var flds = JArray.Parse(fc.Template);
                            foreach (JToken t in flds)
                            {
                                if (t["type"] != null)
                                {
                                    ret.Add(t["fld"].Value<string>(), t["type"].Value<int>());
                                }
                            }


                        }

                    }
                }
            }
            return ret;

        }


    }//eoc

}//eons