namespace Francesco.Recipes.World.Repositories.MediaFile
{
    using Francesco.Recipes.World.Data;
    using Francesco.Recipes.World.Models.BackendModels.MediaFile;
    using Francesco.Recipes.World.Models.BackendModels.Recipe;
    using Francesco.Recipes.World.Repositories.Instruction;
    using Francesco.Recipes.World.Repositories.Recipe;

    public class MediaFileRepository : IMediaFileRepository
    {
        private readonly IInstructionRepository _instructionRepository;
        private readonly FrancescosRecipesWorldDbContext _context;
        private readonly IRecipeRepository _recipeRepository;

        public MediaFileRepository(IInstructionRepository instructionRepository, FrancescosRecipesWorldDbContext context, IRecipeRepository recipeRepository)
        {
            _instructionRepository = instructionRepository;
            _context = context;
            _recipeRepository = recipeRepository;
        }

        public async Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, string fileName, string mimeType, byte[] newMediaData)
        {
            var instruction = await _instructionRepository.GetInstructionAsync(instructionId);

            var mediaToReplace = instruction.MediaFiles.FirstOrDefault(m => m.Id == mediaFileIdToReplace);
            if (mediaToReplace == null)
            {
                throw new InvalidOperationException("The specified media file does not exist.");
            }

            _context.MediaFiles.Remove(mediaToReplace);
            await _context.SaveChangesAsync();

            var newMedia = new MediaFile
            {
                Id = Guid.NewGuid(),
                FileName = fileName,
                MimeType = mimeType,
                Data = newMediaData,
                Instruction = instruction,
            };

            _context.MediaFiles.Add(newMedia);
            await _context.SaveChangesAsync();
        }

        public async Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo)
        {
            if (photo is null)
            {
                throw new ArgumentNullException(nameof(photo));
            }

            var instruction = await _instructionRepository.GetInstructionAsync(instructionId);

            using (var memoryStream = new MemoryStream())
            {
                await photo.CopyToAsync(memoryStream);

                var instructionImage = new MediaFile
                {
                    FileName = photo.FileName,
                    MimeType = photo.ContentType,
                    Data = memoryStream.ToArray(),
                    Instruction = instruction,
                    Recipe = null,
                };

                _context.Add(instructionImage);
                await _context.SaveChangesAsync();
            }
        }

        public async Task UploadRecipeMediaAsync(Guid recipeId, IFormFile mediaFile)
        {
            if (mediaFile == null || mediaFile.Length == 0)
            {
                return;
            }

            try
            {
                var recipe = await _recipeRepository.GetRecipeAsync(recipeId);

                var isImage = mediaFile.ContentType.StartsWith("image/");
                var isVideo = mediaFile.ContentType.StartsWith("video/");

                if (!isImage && !isVideo)
                {
                    throw new InvalidOperationException("Only image or video files are allowed.");
                }

                if (isImage)
                {
                    await RemoveExistingMediaAsync(recipe, "image/");
                }
                else
                {
                    await RemoveExistingMediaAsync(recipe, "video/");
                }

                using var memoryStream = new MemoryStream();
                await mediaFile.CopyToAsync(memoryStream);

                var newMedia = new MediaFile
                {
                    Id = Guid.NewGuid(),
                    FileName = mediaFile.FileName,
                    MimeType = mediaFile.ContentType,
                    Data = memoryStream.ToArray(),
                    Recipe = recipe,
                    Instruction = null,
                };

                _context.MediaFiles.Add(newMedia);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("An error occurred while uploading the media file.", ex);
            }
        }

        private async Task RemoveExistingMediaAsync(Recipe recipe, string mediaTypePrefix)
        {
            var existingMedia = recipe.MediaFiles?.FirstOrDefault(m => m.MimeType?.StartsWith(mediaTypePrefix) == true);
            if (existingMedia != null)
            {
                _context.MediaFiles.Remove(existingMedia);
                await _context.SaveChangesAsync();
            }
        }
    }
}
