public HttpResponseMessage RegisterUser(UserModel model)
        {
            return this.ExecuteOperationAndHandleExceptions(() =>
            {
                this.ValidateUser(model);
                this.ValidateEmail(model.Email);

                var context = new AdvertisementSystemContext();
                var dbUser = GetUserByUsernameOrEmail(model, context);
                if (dbUser != null)
                {
                    throw new InvalidOperationException("This user already exists in the database");
                }
                dbUser = new User()
                {
                    Username = model.Username,
                    Email = model.Email,
                    AuthenticationCode = model.AuthCode
                };
                context.Users.Add(dbUser);

                context.SaveChanges();

                var responseModel = new RegisterUserResponseModel()
                {
                    Id = dbUser.Id,
                    Username = dbUser.Username,
                };

                var response = this.Request.CreateResponse(HttpStatusCode.Created, responseModel);
                return response;
            });
        }
        public IQueryable<CommentModel> GetAllComments()
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                var models = context.Comments.Select(comment => new CommentModel() 
                { 
                    Text = comment.Text,
                    CommentedBy = comment.User.Username,
                    PostDate = comment.CommentDate
                });

                return models;
            }).OrderByDescending(x => x.PostDate);

            return responseMsg;
        }
        public HttpResponseMessage LoginUser(UserModel model)
        {
            return this.ExecuteOperationAndHandleExceptions(() =>
            {
                //this.ValidateUser(model);
                if (model == null)
                {
                    throw new FormatException("invalid username and/or password");
                }
                this.ValidateAuthCode(model.AuthCode);
                try
                {
                    this.ValidateUsername(model.Username);
                }
                catch (Exception ex)
                {
                    this.ValidateEmail(model.Email);
                }

                var context = new AdvertisementSystemContext();
                var username = ((string.IsNullOrEmpty(model.Username)) ? model.Email : model.Username).ToLower();
                var user = context.Users.FirstOrDefault(u => u.Username == username || u.Email == username);
                if (user == null)
                {
                    throw new InvalidOperationException("Invalid username or password");
                }
                if (user.AccessToken == null)
                {
                    user.AccessToken = this.GenerateAccessToken(user.Id);
                    context.SaveChanges();
                }
                var responseModel = new LoginResponseModel()
                {
                    Id = user.Id,
                    Username = user.Username,
                    AccessToken = user.AccessToken
                };
                var response = this.Request.CreateResponse(HttpStatusCode.OK, responseModel);
                return response;
            });
        }
        public IQueryable<TagModel> GetAllTags(
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                this.GetUserByAccessToken(accessToken, context);

                var models =
                    (from tagEnt in context.Tags
                     select new TagModel()
                     {
                         Id = tagEnt.Id,
                         Name = tagEnt.Title,
                         Posts = tagEnt.Advertisements.Count
                     }).OrderBy(tg => tg.Name);

                return models;
            });

            return responseMsg;
        }
        public IQueryable<CategoryModel> GetAllCategories(
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                var models = context.Categories.Select(cat => new CategoryModel()
                {
                    Name = cat.Name,
                    Id=cat.Id,
                    Advertisements = cat.Advertisements.Select(ad => new AdvertisementModel()
                    {
                        Id = ad.Id,
                        Text = ad.Text,
                        Title = ad.Title
                    })
                });

                return models;
            });

            return responseMsg;
        }
        public IQueryable<GetAdvertisementModel> GetAdvertisementsByTag(
            int tagId,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                this.GetUserByAccessToken(accessToken, context);

                var tag = context.Tags.Where(tg => tg.Id == tagId).FirstOrDefault();
                var allModels = context.Advertisements.Where(ps => ps.Tags.Any(tg => tg.Id == tag.Id));

                var filteredModels =
                    (from postEnt in allModels.ToList()
                     select new GetAdvertisementModel()
                     {
                         Id = postEnt.Id,
                         Title = postEnt.Title,
                         Text = postEnt.Text,
                         PostedBy = postEnt.User.Username,
                         PostDate = postEnt.PostDate,
                         Tags = postEnt.Tags.Select(x => x.Title).ToArray(),
                         Comments =
                         from commentEnt in postEnt.Comments
                         select new CommentModel()
                         {
                             Text = commentEnt.Text,
                             PostDate = commentEnt.CommentDate,
                             CommentedBy = commentEnt.User.Username
                         }
                     }).OrderByDescending(ps => ps.PostDate);

                return filteredModels.AsQueryable();
            });

            return responseMsg;
        }
 private User GetUserByUsernameOrEmail(UserModel model, AdvertisementSystemContext context)
 {
     var usernameToLower = model.Username.ToLower();
     var emailToLower = model.Email.ToLower();
     var dbUser = context.Users.FirstOrDefault(u => u.Username == usernameToLower || u.Email == emailToLower);
     return dbUser;
 }
        public HttpResponseMessage LogoutUser(
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))]
            string accessToken)
        {
            return this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                var user = this.GetUserByAccessToken(accessToken, context);
                user.AccessToken = null;
                context.SaveChanges();

                var response = this.Request.CreateResponse(HttpStatusCode.NoContent);
                return response;
            });
        }
        public HttpResponseMessage PostNewTag(
            [FromBody] TagModel model,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                context.Tags.Add(new Tag()
                {
                    Title = model.Name
                });

                context.SaveChanges();

                return this.Request.CreateResponse(HttpStatusCode.Created);
            });

            return responseMsg;
        }
        public HttpResponseMessage PostAdvertisement(
            [FromBody]AdvertisementModel model,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                ValidateText(model.Text);
                ValidateTitle(model.Title);

                var context = new AdvertisementSystemContext();
                using (context)
                {
                    var user = this.GetUserByAccessToken(accessToken, context);

                    if (user == null)
                    {
                        throw new InvalidOperationException("You are not logged in!");
                    }

                    var category = context.Categories.FirstOrDefault(x => x.Id == model.CategoryId);

                    var advert = new Advertisement()
                    {
                        Title = model.Title,
                        Text = model.Text,
                        PostDate = DateTime.Now,
                        ExparationDate = DateTime.Now,
                        User = user,
                        Category = category
                    };

                    HashSet<string> allTags = new HashSet<string>();
                    foreach (var tag in model.Tags)
                    {
                        var lowerTag = tag.ToLower();
                        if (!allTags.Contains(lowerTag))
                        {
                            allTags.Add(lowerTag);
                        }
                    }

                    string[] splitedTitle = model.Title.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var tag in splitedTitle)
                    {
                        var lowerTag = tag.ToLower();
                        if (!allTags.Contains(lowerTag))
                        {
                            allTags.Add(lowerTag);
                        }
                    }

                    foreach (var tag in allTags)
                    {
                        var newTag = context.Tags.Where(t => t.Title == tag).FirstOrDefault();

                        if (newTag == null)
                        {
                            newTag = new Tag()
                            {
                                Title = tag
                            };
                        }

                        advert.Tags.Add(newTag);
                    }

                    context.Advertisements.Add(advert);
                    context.SaveChanges();

                    PostAdvertisementResponseModel responseModel = new PostAdvertisementResponseModel()
                    {
                        Id = advert.Id,
                        Title = advert.Title
                    };

                    return Request.CreateResponse(HttpStatusCode.Created, responseModel);
                }
            });

            return responseMsg;
        }
        public HttpResponseMessage PutNewComment(
            int postId,
            [FromBody] CommentModel model,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                ValidateCommentText(model.Text);

                var context = new AdvertisementSystemContext();

                var user = this.GetUserByAccessToken(accessToken, context);

                if (user == null)
                {
                    throw new InvalidOperationException("You are not logged in!");
                }

                var advert = context.Advertisements.FirstOrDefault(ps => ps.Id == postId);

                if (advert == null)
                {
                    throw new InvalidOperationException("No such post exists");
                }

                Comment newComment = new Comment()
                {
                    Text = model.Text,
                    CommentDate = DateTime.Now,
                    User = user,
                    Post = advert
                };

                context.Comments.Add(newComment);
                context.SaveChanges();

                return Request.CreateResponse(HttpStatusCode.Created);

            });

            return responseMsg;
        }
        public IQueryable<GetAdvertisementModel> GetAllAdvertisements(
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() =>
            {
                var context = new AdvertisementSystemContext();
                this.GetUserByAccessToken(accessToken, context);

                var models =
                    from postEnt in context.Advertisements.ToList()
                    select new GetAdvertisementModel()
                    {
                        Id = postEnt.Id,
                        Title = postEnt.Title,
                        Text = postEnt.Text,
                        PostedBy = postEnt.User.Username,
                        PostDate = postEnt.PostDate,
                        Tags = postEnt.Tags.Select(x => x.Title).ToArray(),
                        Comments =
                        from commentEnt in postEnt.Comments
                        select new CommentModel()
                        {
                            Text = commentEnt.Text,
                            PostDate = commentEnt.CommentDate,
                            CommentedBy = commentEnt.User.Username
                        }
                    };

                return models.OrderByDescending(ps => ps.PostDate);
            });

            return responseMsg.AsQueryable();
        }
        public HttpResponseMessage PostNewCategory(
            [FromBody] CategoryModel model,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            var responseMsg = this.ExecuteOperationAndHandleExceptions(() => 
            {
                var context = new AdvertisementSystemContext();
                var hasCat = context.Categories.FirstOrDefault(x => x.Name == model.Name);

                if (hasCat != null)
                {
                    throw new ArgumentException("Category exists!");
                }

                Category newCat = new Category()
                {
                    Name = model.Name
                };

                context.Categories.Add(newCat);
                context.SaveChanges();

                return this.Request.CreateResponse(HttpStatusCode.Created, newCat);
            });

            return responseMsg;
        }