from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.urls import reverse

from accounts.models import User
from reviews.models import Invite, Review, Source
from rsl.decorators import author_required, main_author_required
from rsl.services.demo_review import can_claim_as_personal, claim_review_as_personal, is_demo_review
from rsl.services.seed import setup_new_review
from rsl.services.slug import unique_review_slug
from rsl.services.workflow import (
    conducting_progress,
    get_conducting_steps,
    get_planning_steps,
    planning_progress,
    screening_funnel,
    steps_for_template,
)


def _user_reviews(user):
    owned = list(Review.objects.filter(owner_id=user.id))
    collab = list(user.collaborations.all())
    seen = {r.id for r in owned}
    return owned + [r for r in collab if r.id not in seen]


@login_required
def index(request):
    reviews = sorted(_user_reviews(request.user), key=lambda r: r.updated_at, reverse=True)
    return render(request, "reviews/index.html", {"reviews": reviews})


@login_required
def create(request):
    if request.method == "POST":
        title = (request.POST.get("title") or "").strip()
        description = (request.POST.get("description") or "").strip()
        if not title:
            messages.warning(request, "Informe um título para a revisão.")
            return render(request, "reviews/create.html")
        slug = unique_review_slug(title, request.user.id)
        review = Review.objects.create(
            slug=slug,
            title=title,
            description=description,
            owner_id=request.user.id,
        )
        setup_new_review(review)
        review.ensure_search_sessions()
        messages.success(request, "Revisão criada com sucesso.")
        return redirect(reverse("planning:protocol", kwargs={"review_id": review.id}))
    return render(request, "reviews/create.html")


@login_required
@author_required
def detail(request, review):
    review.ensure_search_sessions()
    articles = list(review.articles.all())
    stats = {
        "total": len(articles),
        "unclassified": sum(1 for a in articles if a.status == "U"),
        "accepted": sum(1 for a in articles if a.status == "A"),
        "rejected": sum(1 for a in articles if a.status == "R"),
        "duplicated": sum(1 for a in articles if a.status == "D"),
        "final": len(review.get_final_selection_articles()),
    }
    return render(
        request,
        "reviews/detail.html",
        {
            "review": review,
            "stats": stats,
            "funnel": screening_funnel(review),
            "planning_steps": steps_for_template(get_planning_steps(review), review.id),
            "conducting_steps": steps_for_template(get_conducting_steps(review), review.id),
            "planning_pct": planning_progress(review),
            "conducting_pct": conducting_progress(review),
        },
    )


@login_required
@main_author_required
def settings(request, review):
    all_sources = Source.objects.order_by("name")
    if request.method == "POST":
        action = request.POST.get("action")
        if action == "update_meta":
            review.title = (request.POST.get("title") or review.title).strip()
            review.description = request.POST.get("description", "")
            review.is_public = request.POST.get("is_public") == "on"
            review.publish_status = request.POST.get("publish_status", "U")
            review.save()
            messages.success(request, "Configurações da revisão atualizadas.")
        elif action == "update_sources":
            selected_ids = {int(x) for x in request.POST.getlist("sources") if x.isdigit()}
            review.sources.set([s for s in all_sources if s.id in selected_ids])
            review.ensure_search_sessions()
            messages.success(
                request,
                "Fontes bibliográficas atualizadas. Configure as strings de busca por base.",
            )
        elif action == "add_custom_source":
            name = (request.POST.get("custom_source_name") or "").strip()
            url = (request.POST.get("custom_source_url") or "").strip()
            if name:
                source = Source.objects.create(name=name, url=url, is_default=False)
                review.sources.add(source)
                review.ensure_search_sessions()
                messages.success(request, f"Fonte personalizada «{name}» adicionada.")
            else:
                messages.warning(request, "Informe o nome da fonte.")
        elif action == "delete_review":
            review.delete()
            messages.info(request, "Revisão excluída.")
            return redirect("reviews:index")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))

    selected_source_ids = set(review.sources.values_list("id", flat=True))
    invites = Invite.objects.filter(review_id=review.id).order_by("-created_at")
    return render(
        request,
        "reviews/settings.html",
        {
            "review": review,
            "all_sources": all_sources,
            "selected_source_ids": selected_source_ids,
            "invites": invites,
            "can_claim_personal": can_claim_as_personal(review, request.user),
            "is_demo_review": is_demo_review(review),
        },
    )


@login_required
@main_author_required
def invite_collaborator(request, review):
    email = (request.POST.get("email") or "").strip().lower()
    if not email:
        messages.warning(request, "Informe um endereço de e-mail.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    if email == request.user.email.lower():
        messages.warning(request, "Você não pode convidar a si mesmo.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    existing = Invite.objects.filter(
        review_id=review.id, invitee_email=email, status="pending"
    ).first()
    if existing:
        messages.info(request, "Já existe um convite pendente para este e-mail.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    invite = Invite.objects.create(
        review_id=review.id,
        invited_by_id=request.user.id,
        invitee_email=email,
    )
    link = request.build_absolute_uri(
        reverse("invites:accept", kwargs={"code": invite.code})
    )
    messages.success(
        request, f"Convite enviado para {email}. Link para compartilhar: {link}"
    )
    return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))


@login_required
@main_author_required
def remove_collaborator(request, review, user_id):
    user = review.co_authors.filter(pk=user_id).first()
    if user:
        review.co_authors.remove(user)
        messages.info(request, "Colaborador removido.")
    return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))


@login_required
@main_author_required
def transfer_ownership(request, review):
    try:
        user_id = int(request.POST.get("new_owner_id"))
    except (TypeError, ValueError):
        messages.warning(request, "Selecione um colaborador para transferir a autoria.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    new_owner = User.objects.filter(pk=user_id).first()
    if not new_owner or not review.co_authors.filter(pk=new_owner.pk).exists():
        messages.error(request, "Colaborador inválido.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    old_owner = review.owner
    review.co_authors.remove(new_owner)
    if not review.co_authors.filter(pk=old_owner.pk).exists():
        review.co_authors.add(old_owner)
    review.owner = new_owner
    review.save()
    messages.success(request, f"Autoria transferida para {new_owner.name}.")
    return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))


@login_required
@author_required
def claim_personal(request, review):
    if not can_claim_as_personal(review, request.user):
        messages.warning(request, "Esta revisão não pode ser assumida como pessoal.")
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))

    slug = (request.POST.get("slug") or "").strip()
    try:
        claim_review_as_personal(review, request.user, slug=slug or None)
    except ValueError as exc:
        messages.warning(request, str(exc))
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))

    messages.success(
        request,
        "Revisão transferida para sua conta. Ela deixou de ser demonstração; "
        "todos os artigos, triagem e extração foram preservados.",
    )
    return redirect(reverse("reviews:detail", kwargs={"review_id": review.id}))


@login_required
@author_required
def leave(request, review):
    if review.is_main_author(request.user):
        messages.warning(
            request,
            "O autor principal não pode sair. Transfira a autoria ou exclua a revisão.",
        )
        return redirect(reverse("reviews:settings", kwargs={"review_id": review.id}))
    if review.co_authors.filter(pk=request.user.pk).exists():
        review.co_authors.remove(request.user)
        messages.info(request, "Você saiu da revisão.")
    return redirect("reviews:index")
