namespace Francesco.Recipes.World.Controller.Recipe
{
    using Francesco.Recipes.World.Data;
    using Francesco.Recipes.World.Models;
    using Francesco.Recipes.World.Models.BackendModels.Ingredient;
    using Francesco.Recipes.World.Models.BackendModels.Instruction;
    using Francesco.Recipes.World.Models.BackendModels.Recipe;
    using Francesco.Recipes.World.Repositories.Category;
    using Francesco.Recipes.World.Repositories.Favorit;
    using Francesco.Recipes.World.Repositories.Ingredient;
    using Francesco.Recipes.World.Repositories.Instruction;
    using Francesco.Recipes.World.Repositories.MediaFile;
    using Francesco.Recipes.World.Repositories.Recipe;
    using Francesco.Recipes.World.Repositories.Unit;
    using Francesco.Recipes.World.Views.Category;
    using Francesco.Recipes.World.Views.Recipe;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Rendering;

    public class RecipeController : Controller
    {
        private readonly IRecipeRepository _recipeRepository;
        private readonly IUnitRepository _unitRepository;
        private readonly ICategoryRepository _categoryRepository;
        private readonly IIngredientRepository _ingredientRepository;
        private readonly IMediaFileRepository _mediaFileRepository;
        private readonly IInstructionRepository _instructionRepository;
        private readonly IFavoriteRepository _favoriteRepository;
        private readonly FrancescosRecipesWorldDbContext _context;

        public IReadOnlyCollection<Recipe> Recipes { get; set; }

        public RecipeController(
            IRecipeRepository recipeRepository,
            IUnitRepository unitRepository,
            ICategoryRepository categoryRepository,
            IIngredientRepository ingredientRepository,
            IMediaFileRepository mediaFileRepository,
            IInstructionRepository instructionRepository,
            IFavoriteRepository favoriteRepository,
            FrancescosRecipesWorldDbContext context)
        {
            _recipeRepository = recipeRepository;
            _unitRepository = unitRepository;
            _categoryRepository = categoryRepository;
            _ingredientRepository = ingredientRepository;
            Recipes = new List<Recipe>();
            _mediaFileRepository = mediaFileRepository;
            _instructionRepository = instructionRepository;
            _favoriteRepository = favoriteRepository;
            _context = context;
        }

        // GET: /Recipe/{recipeId}/AddOrCreateIngredient
        [HttpGet("{recipeId}/AddOrCreateIngredient")]
        public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId)
        {
            if (recipeId == Guid.Empty)
            {
                return BadRequest("Recipe ID cannot be empty.");
            }

            var units = await _unitRepository.GetAllUnitsAsync();
            var recipeIngredients = await _ingredientRepository.GetIngredientsByRecipeIdAsync(recipeId);
            var ingredients = recipeIngredients.Select(ri => ri.Ingredient).ToList();

            var viewModel = new IngredientViewModel
            {
                RecipeId = recipeId,
                Ingredients = ingredients,
                Units = units.ToList(),
            };

            return View(viewModel);
        }

        // GET: /Recipe/Details/{recipeId}
        [HttpGet("Details/{recipeId}")]
        public async Task<IActionResult> Details(Guid recipeId)
        {
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            return View(recipe);
        }

        // GET: /Recipe/CategoryRecipes
        [HttpGet("CategoryRecipes")]
        public async Task<IActionResult> CategoryRecipes()
        {
            var categories = await _categoryRepository.GetAllCategoriesWithRecipesAsync();

            var viewModel = categories.Select(c => new CategoryRecipesViewModel
            {
                Category = c,
                Recipes = c.Recipes,
            }).ToList();

            return View(viewModel);
        }

        // POST: /Recipe/{recipeId}/AddOrCreateIngredient
        [HttpPost("{recipeId}/AddOrCreateIngredient")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId, string ingredientName, int quantity, Guid unitId)
        {
            if (recipeId == Guid.Empty)
            {
                return BadRequest("Recipe ID cannot be empty.");
            }

            if (quantity <= 0)
            {
                ModelState.AddModelError(nameof(quantity), "Die Menge muss größer als 0 sein.");
            }

            if (!ModelState.IsValid)
            {
                var units = await _unitRepository.GetAllUnitsAsync();
                ViewBag.Units = new SelectList(units, "Id", "Name");
                return View();
            }

            await _recipeRepository.CreateRecipeIngredientAsync(recipeId, ingredientName, quantity, unitId);
            return RedirectToAction("Details", new { id = recipeId });
        }

        // GET: /Recipe/Create/{categoryId}
        [HttpGet("Create/{categoryId}")]
        public async Task<IActionResult> Create(Guid categoryId)
        {
            var category = await _categoryRepository.GetCategoryByIdAsync(categoryId);
            if (category == null)
            {
                return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
            }

            var units = await _unitRepository.GetAllUnitsAsync();

            var viewModel = new CreateRecipeViewModel
            {
                CategoryId = categoryId,
                CategoryName = category.Name,
                IngredientViewModel = new IngredientViewModel
                {
                    RecipeId = Guid.Empty,
                    Ingredients = new List<Ingredient>(),
                    Units = units.ToList(),
                },
                InstructionViewModel = new InstructionViewModel
                {
                    RecipeId = Guid.Empty,
                    Instructions = new List<Instruction>(),
                },
            };
            return View(viewModel);
        }

        // POST: /Recipe/Create/{categoryId}
        [HttpPost("Create/{categoryId}")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(Guid categoryId, CreateRecipeViewModel model)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model), "CreateRecipeViewModel cannot be null.");
            }

            if (string.IsNullOrWhiteSpace(model.Name))
            {
                ModelState.AddModelError(nameof(model.Name), "Name darf nicht leer sein.");
            }

            if (model.Servings <= 0)
            {
                ModelState.AddModelError(nameof(model.Servings), "Anzahl der Portionen muss größer als 0 sein.");
            }

            if (!ModelState.IsValid)
            {
                var category = await _categoryRepository.GetCategoryByIdAsync(categoryId);
                if (category == null)
                {
                    return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
                }

                var units = await _unitRepository.GetAllUnitsAsync();

                if (model.IngredientViewModel == null)
                {
                    model.IngredientViewModel = new IngredientViewModel
                    {
                        RecipeId = Guid.Empty,
                        Ingredients = new List<Ingredient>(),
                        Units = units.ToList(),
                    };
                }
                else
                {
                    model.IngredientViewModel.Units = units.ToList();
                }

                model.CategoryName = category.Name;
                return View(model);
            }

            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                var categoryEntity = await _categoryRepository.GetCategoryByIdAsync(categoryId);
                if (categoryEntity == null)
                {
                    return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
                }

                model.PreparationTime = new TimeSpan(model.PrepHours, model.PrepMinutes, 0);
                model.CookingTime = new TimeSpan(model.CookHours, model.CookMinutes, 0);

                var recipe = await _recipeRepository.CreateRecipeForCategoryAsync(
                    categoryEntity,
                    model.Name,
                    model.Description ?? string.Empty,
                    model.Difficulty,
                    model.Servings,
                    model.PreparationTime,
                    model.CookingTime);

                if (model.Photo != null)
                {
                    await _mediaFileRepository.UploadRecipeMediaAsync(recipe.Id, model.Photo);
                }

                if (model.Video != null)
                {
                    await _mediaFileRepository.UploadRecipeMediaAsync(recipe.Id, model.Video);
                }

                if (model.IngredientViewModel?.Ingredients != null)
                {
                    foreach (var ingredient in model.IngredientViewModel.Ingredients)
                    {
                        var ri = ingredient.RecipeIngredients?.FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(ingredient.Name) && ri?.Quantity > 0 && ri?.Unit?.Id != null)
                        {
                            await _recipeRepository.CreateRecipeIngredientAsync(
                                recipe.Id,
                                ingredient.Name,
                                ri.Quantity,
                                ri.Unit.Id);
                        }
                    }
                }

                if (model.InstructionViewModel?.Instructions != null)
                {
                    for (var i = 0; i < model.InstructionViewModel.Instructions.Count; i++)
                    {
                        var instruction = model.InstructionViewModel.Instructions[i];
                        if (!string.IsNullOrWhiteSpace(instruction.Description))
                        {
                            var fileKey = $"InstructionViewModel.Instructions[{i}].MediaFile";
                            IFormFile? imageFile = null;

                            if (Request.Form.Files.Any(f => f.Name == fileKey))
                            {
                                imageFile = Request.Form.Files[fileKey];
                            }

                            await _instructionRepository.CreateInstructionAsync(
                                recipe.Id,
                                instruction.Description,
                                imageFile);
                        }
                    }
                }

                await transaction.CommitAsync();
                return RedirectToAction("Details", new { recipeId = recipe.Id });
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                return BadRequest(ex.Message);
            }
        }

        // GET: /Recipe/{recipeId}/RemoveIngredient/{ingredientId}
        [HttpGet("{recipeId}/RemoveIngredient/{ingredientId}")]
        public async Task<IActionResult> RemoveIngredient(Guid recipeId, Guid ingredientId)
        {
            var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
            var ingredient = await _ingredientRepository.GetIngredientByIdAsync(ingredientId);

            if (recipe == null || ingredient == null)
            {
                return NotFound("Recipe or Ingredient not found.");
            }

            ViewBag.RecipeId = recipeId;
            ViewBag.IngredientId = ingredientId;

            return View();
        }

        // DELETE: /Recipe/{recipeId}/RemoveIngredient/{ingredientId}
        [HttpDelete("{recipeId}/RemoveIngredient/{ingredientId}")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> RemoveIngredientConfirmed(Guid recipeId, Guid ingredientId)
        {
            await _recipeRepository.RemoveIngredientFromRecipeAsync(recipeId, ingredientId);
            TempData["SuccessMessage"] = "Ingredient removed successfully.";
            return RedirectToAction("Details", new { id = recipeId });
        }

        // GET: /Recipe/{recipeId}/RemoveInstruction/{instructionId}
        [HttpGet("{recipeId}/RemoveInstruction/{instructionId}")]
        public async Task<IActionResult> RemoveInstruction(Guid recipeId, Guid instructionId)
        {
            var recipe = await _recipeRepository.GetRecipeAsync(recipeId);

            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            var instruction = recipe.Instructions?.FirstOrDefault(i => i.Id == instructionId);
            if (instruction == null)
            {
                return NotFound("Instruction not found in the specified recipe.");
            }

            ViewBag.RecipeId = recipeId;
            ViewBag.InstructionId = instructionId;

            return View();
        }

        // DELETE: /Recipe/{recipeId}/RemoveInstruction/{instructionId}
        [HttpDelete("{recipeId}/RemoveInstruction/{instructionId}")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> RemoveInstructionConfirmed(Guid recipeId, Guid instructionId)
        {
            try
            {
                await _instructionRepository.RemoveInstructionFromRecipeAsync(recipeId, instructionId);
                TempData["SuccessMessage"] = "Instruction removed successfully.";
                return RedirectToAction("Details", new { recipeId });
            }
            catch (Exception ex)
            {
                TempData["ErrorMessage"] = $"An error occurred while removing the instruction: {ex.Message}";
                return RedirectToAction("Details", new { recipeId });
            }
        }

        // GET: /Recipe/FilterByDifficulty
        [HttpGet("FilterByDifficulty")]
        public async Task<IActionResult> FilterByDifficulty(Difficulty? selectedDifficulty)
        {
            var recipes = await _recipeRepository.GetRecipesByDifficultyAsync(selectedDifficulty);
            var viewModel = new FilterByDifficultyViewModel
            {
                SelectedDifficulty = selectedDifficulty,
                Recipes = recipes,
            };
            return View(viewModel);
        }

        // GET: /Recipe/{recipeId}/AddInstruction
        [HttpGet("{recipeId}/AddInstruction")]
        public async Task<IActionResult> AddInstruction(Guid recipeId)
        {
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            var instructions = await _instructionRepository.GetInstructionsOfRecipeAsync(recipeId);

            var viewModel = new InstructionViewModel
            {
                RecipeId = recipeId,
                Instructions = instructions,
            };

            return View(viewModel);
        }

        // POST: /Recipe/{recipeId}/AddInstruction
        [HttpPost("{recipeId}/AddInstruction")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> AddInstruction(Guid recipeId, string description, IFormFile? image)
        {
            try
            {
                await _instructionRepository.CreateInstructionAsync(recipeId, description, image);

                var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
                var instructions = recipe?.Instructions?.ToList() ?? new List<Instruction>();

                var viewModel = new InstructionViewModel
                {
                    RecipeId = recipeId,
                    Instructions = instructions,
                };

                return RedirectToAction("AddInstruction", new { recipeId });
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        // GET: /Recipe/Favorites
        [HttpGet("Favorites")]
        public async Task<IActionResult> Favorites()
        {
            var favoriteRecipes = await _favoriteRepository.GetFavoriteRecipesAsync();
            return View(favoriteRecipes);
        }

        // POST: /Recipe/AddFavorite
        [HttpPost("AddFavorite")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> AddFavorite(Guid recipeId)
        {
            await _favoriteRepository.AddFavoriteAsync(recipeId);
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            return PartialView("_FavoriteButton", recipe);
        }

        // POST: /Recipe/RemoveFavorite
        [HttpPost("RemoveFavorite")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> RemoveFavorite(Guid recipeId)
        {
            await _favoriteRepository.RemoveFavoriteAsync(recipeId);
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            return PartialView("_FavoriteButton", recipe);
        }

        // GET: /Recipe/{recipeId}/GetIngredients
        [HttpGet("{recipeId}/GetIngredients")]
        public async Task<IActionResult> GetIngredients(Guid recipeId)
        {
            var recipeIngredients = (await _ingredientRepository.GetIngredientsByRecipeIdAsync(recipeId)).Select(ri => ri.Ingredient).ToList();
            var units = await _unitRepository.GetAllUnitsAsync();

            var viewModel = new IngredientViewModel
            {
                RecipeId = recipeId,
                Ingredients = recipeIngredients,
                Units = units,
            };

            return PartialView("_IngredientsPartial", viewModel);
        }

        // GET: /Recipe/{recipeId}/AdjustableIngredients
        [HttpGet("{recipeId}/AdjustableIngredients")]
        public async Task<IActionResult> GetAdjustableIngredients(Guid recipeId)
        {
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            return PartialView("_AdjustableIngredientsPartial", recipe);
        }

        // GET: /Recipe/{recipeId}/Delete
        [HttpGet("{recipeId}/Delete")]
        public async Task<IActionResult> Delete(Guid recipeId)
        {
            var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
            if (recipe == null)
            {
                return NotFound("Recipe not found.");
            }

            return View(recipe);
        }

        // DELETE: /Recipe/{recipeId}/Delete
        [HttpDelete("{recipeId}/Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(Guid recipeId)
        {
            var deleted = await _recipeRepository.DeleteRecipeAsync(recipeId);

            if (deleted)
            {
                TempData["SuccessMessage"] = "Rezept wurde erfolgreich gelöscht.";
                return RedirectToAction("Index", "Home");
            }
            else
            {
                TempData["ErrorMessage"] = "Rezept nicht gefunden oder konnte nicht gelöscht werden.";
                return RedirectToAction("Details", new { recipeId });
            }
        }
    }
}
