import logging
import redis
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin, \
    PermissionRequiredMixin
from django.http import JsonResponse, HttpResponse, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.views.decorators.http import require_POST
from django.views.generic import CreateView, UpdateView, DeleteView
from django.views.generic.edit import ModelFormMixin

from common.decorators import ajax_required
from .forms import CommentForm, ArticleForm
from .models import Article, Comment
from .selectors import get_article_by_slug, get_parent_comment, \
    get_comments_by_id, \
    get_moderation_articles, get_published_articles, get_draft_articles, \
    get_film_articles, get_anime_articles, \
    get_game_articles
from .services import create_comment_form, create_reply_form, is_author, \
    paginate_articles, save_comment, \
    paginate_comments, search_results

logger = logging.getLogger(__name__)

r = redis.StrictRedis(host=settings.REDIS_HOST,
                      port=settings.REDIS_PORT,
                      db=settings.REDIS_DB)


def articles_redirect(request):
    """
    Redirect to main page
    """
    return redirect('articles:all_articles', permanent=True)


def publish_list(request):
    """
    Show all published articles
    """
    object_list = get_published_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


@permission_required('articles.can_moderate_article', raise_exception=True)
def moderation_list(request):
    """
    Show articles on moderation
    """
    object_list = get_moderation_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


@permission_required('articles.can_draft_article', raise_exception=True)
def draft_list(request):
    """
    Show all draft articles
    """
    object_list = get_draft_articles(request)
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


def film_articles_list(request):
    """
    Show articles in category films
    """
    object_list = get_film_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'film',
                                                       'articles': articles})


def anime_articles_list(request):
    """
    Show articles in category anime
    """
    object_list = get_anime_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'anime',
                                                       'articles': articles})


def game_articles_list(request):
    """
    Show articles in category games
    """
    object_list = get_game_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'game',
                                                       'articles': articles})


def article_detail(request, slug):
    """
    Show details of the article
    """
    article = get_article_by_slug(slug=slug, annotate=True)
    comment_form = CommentForm()
    total_views = r.incr(f'article:{article.id}:views')
    return render(request, 'articles/post/detail.html',
                  {'article': article,
                   'section': article.category,
                   'comment_form': comment_form,
                   'total_views': total_views})


def comments_list(request, article_id):
    """
    Show all paginated comments of the article
    """
    comments = get_comments_by_id(article_id)
    paginated_comments = paginate_comments(request, comments)
    if paginated_comments is None:
        return HttpResponse('')
    return render(request, 'articles/comment/partial_comments_all.html',
                  {'comments': paginated_comments})


@ajax_required
@login_required
def reply_comment(request, comment_id):
    """
    Create reply of the parent comment
    """
    parent_comment = get_parent_comment(comment_id)
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        create_reply_form(request, comment_form, parent_comment)
    else:
        comment_form = CommentForm()
    return save_comment(request, 'articles/comment/partial_comment_create.html',
                        comment_form, action='reply', comment_id=comment_id)


@ajax_required
@login_required
def create_comment(request, article_id):
    """
    Create an article comment
    """
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        create_comment_form(request, comment_form, article_id)
    else:
        comment_form = CommentForm()
    return save_comment(request, 'articles/comment/partial_comments_all.html',
                        comment_form)


@ajax_required
def edit_comment(request, comment_id):
    """
    Edit an article comment
    """
    instance_comment = get_object_or_404(Comment, id=comment_id)
    if not is_author(request, instance_comment):
        return HttpResponseForbidden()
    if request.method == 'POST':
        comment_form = CommentForm(instance=instance_comment, data=request.POST)
    else:
        comment_form = CommentForm(instance=instance_comment)
    return save_comment(request, 'articles/comment/partial_comment_edit.html',
                        comment_form)


@ajax_required
def delete_comment(request, comment_id):
    """
    delete an article comment
    """
    comment = get_object_or_404(Comment, id=comment_id)
    if not is_author(request, comment):
        return HttpResponseForbidden()
    article_id = comment.article_id
    comments = get_comments_by_id(article_id)
    data = dict()
    if request.method == 'POST':
        comment.delete()
        data['form_is_valid'] = True
        data['html_comments_all'] = render_to_string(
            'articles/comment/partial_comments_all.html', {
                'comments': comments, 'user': request.user})
    else:
        data['html_form'] = render_to_string(
            'articles/comment/partial_comment_delete.html',
            {'comment': comment, 'user': request.user}, request=request)
    return JsonResponse(data)


class ArticleBaseValidation(ModelFormMixin):
    """
    Base class of article's validation
    """

    def form_valid(self, form):
        """Check article's status in a form and assign it"""
        form.instance.author = self.request.user
        if 'draft' in self.request.POST:
            form.instance.status = 'draft'
        elif 'moderation' in self.request.POST:
            form.instance.status = 'moderation'
        elif 'publish' in self.request.POST:
            form.instance.status = 'publish'
        return super().form_valid(form)


class CreateArticle(PermissionRequiredMixin, LoginRequiredMixin, CreateView,
                    ArticleBaseValidation):
    """
    Клас створення статті (необхідний відповідний дозвіл)
    """
    form_class = ArticleForm
    model = Article
    template_name = 'articles/post/create_article.html'
    permission_required = 'articles.add_article'


class UpdateArticle(PermissionRequiredMixin, LoginRequiredMixin, UpdateView,
                    ArticleBaseValidation):
    """
    Клас редагування статті (необхідний дозвіл на це)
    """
    model = Article
    form_class = ArticleForm
    template_name = 'articles/post/update_article.html'
    permission_required = 'articles.change_article'


class DeleteArticle(PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
    """
    Видалення статті (необхідний дозвіл або статус персонала)
    """
    model = Article
    template_name = 'articles/post/delete_article.html'
    success_url = reverse_lazy('articles:all_articles')
    permission_required = 'articles.delete_article'


@require_POST
@login_required
def article_like(request):
    """
    Function to like the article
    """
    article_id = request.POST.get('id')
    action = request.POST.get('action')
    if article_id and action:
        article = get_object_or_404(Article, id=article_id)
        if action == 'like':
            article.users_like.add(request.user)
        else:
            article.users_like.remove(request.user)
        return JsonResponse({'status': 'ok'})
    return JsonResponse({'status': '404'})


def article_search(request):
    """Пошук статті і використанням вектору пошуку (за полями заголовку і тексту з
    ваговими коефіцієнтами 1 та 0.4 відповідно. Пошуковий набір проходить стемінг.
    При пошуку враховується близькість шуканих слів одне до одного"""
    query = ''
    results = []
    if 'query' in request.GET:
        results, query = search_results(request)
    return render(request, 'articles/post/search.html', {'query': query,
                                                         'results': results})


@login_required
@require_POST
def bookmark_article(request):
    """
    Add or remove article from bookmarked articles of user
    """
    article_id = request.POST.get('id')
    action = request.POST.get('action')
    if article_id and action:
        article = get_object_or_404(Article, id=article_id)
        if action == 'bookmark':
            article.users_bookmark.add(request.user)
        else:
            article.users_bookmark.remove(request.user)
        return JsonResponse({'status': 'ok'})
    return JsonResponse({'status': '404'})

import logging
import redis
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin, \
    PermissionRequiredMixin
from django.http import JsonResponse, HttpResponse, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.views.decorators.http import require_POST
from django.views.generic import CreateView, UpdateView, DeleteView
from django.views.generic.edit import ModelFormMixin

from common.decorators import ajax_required
from .forms import CommentForm, ArticleForm
from .models import Article, Comment
from .selectors import get_article_by_slug, get_parent_comment, \
    get_comments_by_id, \
    get_moderation_articles, get_published_articles, get_draft_articles, \
    get_film_articles, get_anime_articles, \
    get_game_articles
from .services import create_comment_form, create_reply_form, is_author, \
    paginate_articles, save_comment, \
    paginate_comments, search_results

logger = logging.getLogger(__name__)

r = redis.StrictRedis(host=settings.REDIS_HOST,
                      port=settings.REDIS_PORT,
                      db=settings.REDIS_DB)


def articles_redirect(request):
    """
    Redirect to main page
    """
    return redirect('articles:all_articles', permanent=True)


def publish_list(request):
    """
    Show all published articles
    """
    object_list = get_published_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


@permission_required('articles.can_moderate_article', raise_exception=True)
def moderation_list(request):
    """
    Show articles on moderation
    """
    object_list = get_moderation_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


@permission_required('articles.can_draft_article', raise_exception=True)
def draft_list(request):
    """
    Show all draft articles
    """
    object_list = get_draft_articles(request)
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'articles': articles})


def film_articles_list(request):
    """
    Show articles in category films
    """
    object_list = get_film_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'film',
                                                       'articles': articles})


def anime_articles_list(request):
    """
    Show articles in category anime
    """
    object_list = get_anime_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'anime',
                                                       'articles': articles})


def game_articles_list(request):
    """
    Show articles in category games
    """
    object_list = get_game_articles()
    articles = paginate_articles(request, object_list)
    return render(request, 'articles/post/list.html', {'section': 'game',
                                                       'articles': articles})


def article_detail(request, slug):
    """
    Show details of the article
    """
    article = get_article_by_slug(slug=slug, annotate=True)
    comment_form = CommentForm()
    total_views = r.incr(f'article:{article.id}:views')
    return render(request, 'articles/post/detail.html',
                  {'article': article,
                   'section': article.category,
                   'comment_form': comment_form,
                   'total_views': total_views})


def comments_list(request, article_id):
    """
    Show all paginated comments of the article
    """
    comments = get_comments_by_id(article_id)
    paginated_comments = paginate_comments(request, comments)
    if paginated_comments is None:
        return HttpResponse('')
    return render(request, 'articles/comment/partial_comments_all.html',
                  {'comments': paginated_comments})


@ajax_required
@login_required
def reply_comment(request, comment_id):
    """
    Create reply of the parent comment
    """
    parent_comment = get_parent_comment(comment_id)
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        create_reply_form(request, comment_form, parent_comment)
    else:
        comment_form = CommentForm()
    return save_comment(request, 'articles/comment/partial_comment_create.html',
                        comment_form, action='reply', comment_id=comment_id)


@ajax_required
@login_required
def create_comment(request, article_id):
    """
    Create an article comment
    """
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        create_comment_form(request, comment_form, article_id)
    else:
        comment_form = CommentForm()
    return save_comment(request, 'articles/comment/partial_comments_all.html',
                        comment_form)


@ajax_required
def edit_comment(request, comment_id):
    """
    Edit an article comment
    """
    instance_comment = get_object_or_404(Comment, id=comment_id)
    if not is_author(request, instance_comment):
        return HttpResponseForbidden()
    if request.method == 'POST':
        comment_form = CommentForm(instance=instance_comment, data=request.POST)
    else:
        comment_form = CommentForm(instance=instance_comment)
    return save_comment(request, 'articles/comment/partial_comment_edit.html',
                        comment_form)


@ajax_required
def delete_comment(request, comment_id):
    """
    delete an article comment
    """
    comment = get_object_or_404(Comment, id=comment_id)
    if not is_author(request, comment):
        return HttpResponseForbidden()
    article_id = comment.article_id
    comments = get_comments_by_id(article_id)
    data = dict()
    if request.method == 'POST':
        comment.delete()
        data['form_is_valid'] = True
        data['html_comments_all'] = render_to_string(
            'articles/comment/partial_comments_all.html', {
                'comments': comments, 'user': request.user})
    else:
        data['html_form'] = render_to_string(
            'articles/comment/partial_comment_delete.html',
            {'comment': comment, 'user': request.user}, request=request)
    return JsonResponse(data)


class ArticleBaseValidation(ModelFormMixin):
    """
    Base class of article's validation
    """

    def form_valid(self, form):
        """Check article's status in a form and assign it"""
        form.instance.author = self.request.user
        if 'draft' in self.request.POST:
            form.instance.status = 'draft'
        elif 'moderation' in self.request.POST:
            form.instance.status = 'moderation'
        elif 'publish' in self.request.POST:
            form.instance.status = 'publish'
        return super().form_valid(form)


class CreateArticle(PermissionRequiredMixin, LoginRequiredMixin, CreateView,
                    ArticleBaseValidation):
    """
    Клас створення статті (необхідний відповідний дозвіл)
    """
    form_class = ArticleForm
    model = Article
    template_name = 'articles/post/create_article.html'
    permission_required = 'articles.add_article'


class UpdateArticle(PermissionRequiredMixin, LoginRequiredMixin, UpdateView,
                    ArticleBaseValidation):
    """
    Клас редагування статті (необхідний дозвіл на це)
    """
    model = Article
    form_class = ArticleForm
    template_name = 'articles/post/update_article.html'
    permission_required = 'articles.change_article'


class DeleteArticle(PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
    """
    Видалення статті (необхідний дозвіл або статус персонала)
    """
    model = Article
    template_name = 'articles/post/delete_article.html'
    success_url = reverse_lazy('articles:all_articles')
    permission_required = 'articles.delete_article'


@require_POST
@login_required
def article_like(request):
    """
    Function to like the article
    """
    article_id = request.POST.get('id')
    action = request.POST.get('action')
    if article_id and action:
        article = get_object_or_404(Article, id=article_id)
        if action == 'like':
            article.users_like.add(request.user)
        else:
            article.users_like.remove(request.user)
        return JsonResponse({'status': 'ok'})
    return JsonResponse({'status': '404'})


def article_search(request):
    """Пошук статті і використанням вектору пошуку (за полями заголовку і тексту з
    ваговими коефіцієнтами 1 та 0.4 відповідно. Пошуковий набір проходить стемінг.
    При пошуку враховується близькість шуканих слів одне до одного"""
    query = ''
    results = []
    if 'query' in request.GET:
        results, query = search_results(request)
    return render(request, 'articles/post/search.html', {'query': query,
                                                         'results': results})


@login_required
@require_POST
def bookmark_article(request):
    """
    Add or remove article from bookmarked articles of user
    """
    article_id = request.POST.get('id')
    action = request.POST.get('action')
    if article_id and action:
        article = get_object_or_404(Article, id=article_id)
        if action == 'bookmark':
            article.users_bookmark.add(request.user)
        else:
            article.users_bookmark.remove(request.user)
        return JsonResponse({'status': 'ok'})
    return JsonResponse({'status': '404'})

Redirect to main page Show all published articles Show articles on moderation Show all draft articles Show articles in category films Show articles in category anime Show articles in category games Show details of the article Show all paginated comments of the article Create reply of the parent comment Create an article comment Edit an article comment delete an article comment Base class of article's validation Check article's status in a form and assign it Клас створення статті (необхідний відповідний дозвіл) Клас редагування статті (необхідний дозвіл на це) Видалення статті (необхідний дозвіл або статус персонала) Function to like the article Пошук статті і використанням вектору пошуку (за полями заголовку і тексту з     ваговими коефіцієнтами 1 та 0.4 відповідно. Пошуковий набір проходить стемінг.     При пошуку враховується близькість шуканих слів одне до одного Add or remove article from bookmarked articles of user