import json

from typing import Any
from datetime import datetime as dt
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.core import serializers
from django.views import generic
from django.views.decorators.csrf import ensure_csrf_cookie
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin
from django.core.exceptions import PermissionDenied

from webapp.forms import ArticleForm, CommentForm
from webapp.models import Article


class ArticleDetailView(PermissionRequiredMixin, generic.DetailView):
    pk_url_kwarg = 'id'
    model = Article
    template_name = 'articles/detail.html'
    permission_required = 'webapp.can_eat_pizza'
    
    def get_context_data(self, **kwargs):
        comments = self.object.comments.order_by('-created_at')
        
        return super().get_context_data(
            **kwargs, 
            comments=comments,
            comments_form=CommentForm,
            messages=self.request.session.get('index:messages', []),
        )


class ArticleCreateView(PermissionRequiredMixin, generic.CreateView):
    model = Article
    template_name = 'articles/article.html'
    form_class = ArticleForm
    permission_required = 'webapp.add_article'
    permission_denied_message = 'У вас нет прав'
    
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('articles_detail', kwargs={'id': self.object.id})


class ArticleUpdateView(PermissionRequiredMixin, generic.UpdateView):
    template_name = 'articles/article.html'
    model = Article
    form_class = ArticleForm
    context_object_name = 'article'
    pk_url_kwarg = 'id'
    extra_context = {'btn_text': 'Update'}
    permission_required = 'webapp.change_article'

    def has_permission(self) -> bool:
        return super().has_permission() or self.get_object().author == self.request.user

    def get_success_url(self):
        return reverse('articles_detail', kwargs={'id': self.object.id})


class DeleteView(generic.View):
    template_name = None
    model = None
    context_object_name = None
    pk_url_kwarg = None
    success_url = None
    
    def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        self.object = self.get_object()
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.render_to_response()

    def post(self, request, *args, **kwargs):
        self.perform_delete()
        return redirect(self.get_success_url())

    def perform_delete(self):
        self.object.delete()

    def get_object(self):
        return get_object_or_404(self.model, id=self.kwargs.get(self.pk_url_kwarg))
    
    def get_context_data(self, **kwargs):
        kwargs[self.context_object_name] = self.object
        return kwargs

    def get_success_url(self):
        return self.success_url

    def render_to_response(self, **kwargs):
        return render(
            self.request, 
            self.template_name, 
            context=self.get_context_data(**kwargs),
        )


class ArticleDeleteView(generic.DeleteView):
    model = Article
    pk_url_kwarg = 'id'
    success_url = reverse_lazy('index')


@ensure_csrf_cookie
def get_token_view(request, *args, **kwargs):
    if request.method == 'GET':
        return HttpResponse()

    return HttpResponseNotAllowed('Only GET requests allowed')


def json_echo_view(request, *args, **kwargs):
    answer = {
        'time': dt.now().strftime('%d-%m-%Y %H:%M:%S'),
        'method': request.method,
    }

    if request.body:
        answer['content'] = json.loads(request.body)

    return JsonResponse(answer)


def index_api_view(request, *args, **kwargs):
    if request.method == 'GET':
        articles = Article.objects.all()
        serialized_articles = serializers.serialize('json', articles)

        return HttpResponse(serialized_articles)


def articles_create_api_view(request, *args, **kwargs):
    if request.method == 'POST':
        if request.body:
            article_data = json.loads(request.body)

            article = Article.objects.create(**article_data)
            return HttpResponse(serializers.serialize('json', [article]))

        else:
            return JsonResponse({'error': 'No data provided!'}, status_code=400)
