namespace inutralia.ViewModels
{
    using inutralia.Models;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    using System.Collections.Generic;
    using System.Linq;

    public class CustomMenuViewModel : MenuBaseViewModel
    {
        public CustomMenuViewModel() : this(new Models.Menu()) { }

        public CustomMenuViewModel(Models.Menu menu) : base(menu) { }

        /// <summary>
        /// El menú guardado en el almacenamiento local
        /// </summary>
        protected LocalMenu _LocalMenu = null;

        /// <summary>
        /// El ViewModel devuelve el menú local como menú semanal
        /// </summary>
        public override MenuBase WeekMenu => _LocalMenu;

        /// <summary>
        /// Indica si existe menú en el almacenamiento local
        /// </summary>
        public bool HasMenu => _LocalMenu != null;

        /// <summary>
        /// Indica que no hay datos de menú
        /// </summary>
        public bool NoMenuPanel => !HasMenu && IsNotBusy;

        /// <summary>
        /// Los índices de plato seleccionado en el día mostrado
        /// </summary>
        public LocalMenu.MealSelections CurrentSelections
        {
            get
            {
                if ((Index >= 0) && (Index < _LocalMenu?.DaySelections?.Length))
                {
                    var retVal = _LocalMenu.DaySelections[Index];
                    if (retVal == null)
                    {
                        retVal = new LocalMenu.MealSelections();
                        _LocalMenu.DaySelections[Index] = retVal;
                    } //endif

                    return retVal;
                } //endif

                return null;
            }
        }

        #region Opciones seleccionadas

        public bool IsLunchFirstOption1
        {
            get { return CurrentSelections?.LunchFirstIndex == 0; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.LunchFirstIndex == 0;
                    if (value != current)
                    {
                        CurrentSelections.LunchFirstIndex = value ? 0 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsLunchFirstOption1");
                        OnPropertyChanged("IsLunchFirstOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsLunchFirstOption2
        {
            get { return CurrentSelections?.LunchFirstIndex == 1; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.LunchFirstIndex == 1;
                    if (value != current)
                    {
                        CurrentSelections.LunchFirstIndex = value ? 1 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsLunchFirstOption1");
                        OnPropertyChanged("IsLunchFirstOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsLunchSecondOption1
        {
            get { return CurrentSelections?.LunchSecondIndex == 0; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.LunchSecondIndex == 0;
                    if (value != current)
                    {
                        CurrentSelections.LunchSecondIndex = value ? 0 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsLunchSecondOption1");
                        OnPropertyChanged("IsLunchSecondOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsLunchSecondOption2
        {
            get { return CurrentSelections?.LunchSecondIndex == 1; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.LunchSecondIndex == 1;
                    if (value != current)
                    {
                        CurrentSelections.LunchSecondIndex = value ? 1 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsLunchSecondOption1");
                        OnPropertyChanged("IsLunchSecondOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsDinnerFirstOption1
        {
            get { return CurrentSelections?.DinnerFirstIndex == 0; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.DinnerFirstIndex == 0;
                    if (value != current)
                    {
                        CurrentSelections.DinnerFirstIndex = value ? 0 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsDinnerFirstOption1");
                        OnPropertyChanged("IsDinnerFirstOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsDinnerFirstOption2
        {
            get { return CurrentSelections?.DinnerFirstIndex == 1; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.DinnerFirstIndex == 1;
                    if (value != current)
                    {
                        CurrentSelections.DinnerFirstIndex = value ? 1 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsDinnerFirstOption1");
                        OnPropertyChanged("IsDinnerFirstOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsDinnerSecondOption1
        {
            get { return CurrentSelections?.DinnerSecondIndex == 0; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.DinnerSecondIndex == 0;
                    if (value != current)
                    {
                        CurrentSelections.DinnerSecondIndex = value ? 0 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsDinnerSecondOption1");
                        OnPropertyChanged("IsDinnerSecondOption2");
                    } //endif
                } //endif
            }
        }

        public bool IsDinnerSecondOption2
        {
            get { return CurrentSelections?.DinnerSecondIndex == 1; }
            set
            {
                if (CurrentSelections != null)
                {
                    bool current = CurrentSelections.DinnerSecondIndex == 1;
                    if (value != current)
                    {
                        CurrentSelections.DinnerSecondIndex = value ? 1 : -1;
                        SelectionChanged();
                        OnPropertyChanged("IsDinnerSecondOption1");
                        OnPropertyChanged("IsDinnerSecondOption2");
                    } //endif
                } //endif
            }
        }

        #endregion

        protected bool _SelectionHasBeenChanged = false;

        public async Task LoadData()
        {
            bool ShouldLoadRecipes = false;

            IsBusy = true;
            OnPropertyChanged("NoMenuPanel");

            // Obtener menú local
            var lista = await App.LocalData.RefreshListAsync<LocalMenu>();
            if (lista.Count > 0)
            {
                _LocalMenu = lista[0];
                OnDataRefreshed();
                OnPropertyChanged("HasMenu");
            } //endif

            // Obtener menú del servidor
            if (await App.API.RefreshItemAsync(Model))
            {
                Models.Menu ServerMenu = Model as Models.Menu;

                if (_LocalMenu == null)
                {
                    // No hay menú local, copiar directamente el del servidor
                    _LocalMenu = new LocalMenu();
                    _LocalMenu.AssignFromMenu(ServerMenu);
                    await App.LocalData.UpdateItemAsync(_LocalMenu, true);

                    // Indicar que hay que cargar recetas
                    ShouldLoadRecipes = true;
                }
                else
                {
                    // Hay menú local. Comprobar si el del servidor es superior al último recibido
                    if (_LocalMenu.LastReceivedMenuId != ServerMenu.Id)
                    {
                        // En ese caso, preguntar si se quiere utilizar el nuevo ...
                        if (await Application.Current?.MainPage.DisplayAlert("Nuevo menú semanal", "Hay un nuevo menú semanal disponible", "Utilizarlo", "Descartarlo"))
                        {
                            //Eliminamos el dato local, ya que le UpdateItemAsync no es capaz de reconocer que se trata del mismo dato, ya que cambia el ID
                            await App.LocalData.DeleteItemAsync(_LocalMenu);

                            // No hay menú local, copiar directamente el del servidor
                            _LocalMenu = new LocalMenu();

                            // Asignamos el menú como el actual 
                            _LocalMenu.AssignFromMenu(ServerMenu);

                            //Creamos el dato nuevo
                            await App.LocalData.UpdateItemAsync(_LocalMenu, true);

                            // Indicar que hay que cargar recetas
                            ShouldLoadRecipes = true;
                        } //endif

                        // Guardar el id del menú semanal recibido, para no volver a preguntar
                        _LocalMenu.LastReceivedMenuId = ServerMenu.Id;

                        // Guardar el menú local
                        await App.LocalData.UpdateItemAsync(_LocalMenu, false);
                    } //endif
                } //endif
            } //endif

            // Si es necesario, cargar recetas y actualizar
            if (ShouldLoadRecipes)
            {
                await LoadAllRecipesAsync();
                await App.LocalData.UpdateItemAsync(_LocalMenu, false);
            } //endif

            // Actualizar día seleccionado
            OnDataRefreshed();
            OnPropertyChanged("HasMenu");

            _SelectionHasBeenChanged = false;
            IsBusy = false;
            OnPropertyChanged("NoMenuPanel");
        }

        protected void SelectionChanged()
        {
            _SelectionHasBeenChanged = true;
        }

        public async Task UpdateShoppingListAsync()
        {
            if (_SelectionHasBeenChanged)
            {
                IsBusy = true;

                // Esperar a que las recetas estén listas
                while (IsLoadingRecipes)
                    await Task.Delay(200);

                // Preparar la lista de ingredientes a partir de las recetas seleccionadas

                //var ingredients = new HashSet<string>();

                var ingredients = new HashSet<Ingredient>();

                for (int i = 0; i < _LocalMenu?.DaySelections?.Length; i++)
                {
                    if (i < _LocalMenu.Days.Count)
                    {
                        var Day = _LocalMenu.Days[i];
                        var meals = _LocalMenu.DaySelections[i];
                        if (meals != null)
                        {
                            AddRecipe(ingredients, Day.LunchFirst, meals.LunchFirstIndex);
                            AddRecipe(ingredients, Day.LunchSecond, meals.LunchSecondIndex);
                            AddRecipe(ingredients, Day.DinnerFirst, meals.DinnerFirstIndex);
                            AddRecipe(ingredients, Day.DinnerSecond, meals.DinnerSecondIndex);
                        } //endif
                    } //endif
                } //endfor

                // Eliminar de la lista de la compra los ingredientes que no estén en los encontrados
                var listaCompra = await App.LocalData.RefreshListAsync<ShoppingList>();
                //foreach (var ing in listaCompra.Where(sl => sl.FromMenus && !ingredients.Contains(sl.Text)))
                foreach (var ing in listaCompra.Where(sl => sl.FromMenus && !ingredients.Any(o => o.Name.Contains(sl.Text))))
                {
                    await App.LocalData.DeleteItemAsync(ing);
                } //endforeach

                // Añadir a la lista de la compra los que falten

                //foreach (string ing in ingredients.Where (i => listaCompra.Find (sl => sl.FromMenus && sl.Text.Equals (i)) == null))
                foreach (Ingredient ing in ingredients.Where(i => listaCompra.Find(sl => sl.FromMenus && sl.Text.Equals(i) && sl.Id.Equals(i)) == null))
                {
                    //await App.LocalData.UpdateItemAsync(new ShoppingList() { FromMenus = true, Select = false, Text = ing }, true);
                    await App.LocalData.UpdateItemAsync(new ShoppingList() { FromMenus = true, Select = false, Text = ing.Name, Id = ing.Id }, true);
                } //endforeach

                await App.LocalData.UpdateItemAsync(_LocalMenu);

                IsBusy = false;

                _SelectionHasBeenChanged = false;
            } //endif
        }

        //protected void AddRecipe ( HashSet<string> ingredients, IList<Recipe> recipes, int index)
        protected async void AddRecipe(HashSet<Ingredient> ingredients, IList<Recipe> recipes, int index)
        {
            if ((index >= 0) && (index < recipes.Count))
            {
                if (recipes[index].Ingredients != null)
                {
                    foreach (var ing in recipes[index].Ingredients)
                    {
                        // Lambda que elimina los ingredientes que se repiten al añadir recetas
                        if (!ingredients.Any(o => o.Name.Contains(ing.Name)))
                            //ingredients.Add (ing.Name);
                            ingredients.Add(new Ingredient() { Name = ing.Name, Id = ing.Id });
                    } //endforeach
                } //endif
            }//endif
            await App.LocalData.RefreshListAsync<ShoppingList>();
        }

        protected override void OnDataRefreshed()
        {
            base.OnDataRefreshed();

            OnPropertyChanged("CurrentSelections");
            OnPropertyChanged("IsLunchFirstOption1");
            OnPropertyChanged("IsLunchFirstOption2");
            OnPropertyChanged("IsLunchSecondOption1");
            OnPropertyChanged("IsLunchSecondOption2");
            OnPropertyChanged("IsDinnerFirstOption1");
            OnPropertyChanged("IsDinnerFirstOption2");
            OnPropertyChanged("IsDinnerSecondOption1");
            OnPropertyChanged("IsDinnerSecondOption2");
        }

        protected override void OnIndexChanged()
        {
            base.OnIndexChanged();

            OnPropertyChanged("CurrentSelections");
            OnPropertyChanged("IsLunchFirstOption1");
            OnPropertyChanged("IsLunchFirstOption2");
            OnPropertyChanged("IsLunchSecondOption1");
            OnPropertyChanged("IsLunchSecondOption2");
            OnPropertyChanged("IsDinnerFirstOption1");
            OnPropertyChanged("IsDinnerFirstOption2");
            OnPropertyChanged("IsDinnerSecondOption1");
            OnPropertyChanged("IsDinnerSecondOption2");
        }
    }
}
