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 reviews.models import (
    DataExtractionField,
    DataExtractionLookup,
    Hypothesis,
    Keyword,
    QualityAnswer,
    QualityQuestion,
    Question,
    SelectionCriteria,
    Source,
)
from rsl.decorators import author_required
from rsl.services.search_sessions import (
    copy_base_to_sources,
    generate_and_save_base,
    get_search_overview,
    save_base_string,
    save_source_string,
)
from rsl.services.search_string import import_keywords_from_picoc


@login_required
@author_required
def index(request, review):
    return redirect(reverse("planning:protocol", kwargs={"review_id": review.id}))


@login_required
@author_required
def protocol(request, review):
    if request.method == "POST":
        action = request.POST.get("action")
        if action == "save_protocol":
            review.objective = request.POST.get("objective", "")
            review.population = request.POST.get("population", "")
            review.intervention = request.POST.get("intervention", "")
            review.comparison = request.POST.get("comparison", "")
            review.outcome = request.POST.get("outcome", "")
            review.context = request.POST.get("context", "")
            review.save()
            messages.success(request, "Protocolo salvo.")
        elif action == "import_keywords":
            merge = request.POST.get("merge_keywords") == "on"
            import_keywords_from_picoc(review, merge=merge)
            messages.success(request, "Palavras-chave importadas dos campos PICOC.")
        elif action == "generate_search_string":
            generate_and_save_base(review)
            messages.success(request, "String de busca geral gerada a partir das palavras-chave.")
        elif action == "save_search_string":
            save_base_string(review, request.POST.get("search_string", ""))
            messages.success(request, "String de busca geral salva.")
        elif action == "add_question":
            text = (request.POST.get("question") or "").strip()
            if text:
                order = review.questions.count()
                Question.objects.create(review_id=review.id, question=text, order=order)
        elif action == "delete_question":
            q = Question.objects.filter(
                id=request.POST.get("question_id"), review_id=review.id
            ).first()
            if q:
                q.delete()
        elif action == "add_hypothesis":
            text = (request.POST.get("statement") or "").strip()
            htype = request.POST.get("hypothesis_type", "S")
            if htype not in ("S", "W"):
                htype = "S"
            if text:
                order = Hypothesis.objects.filter(
                    review_id=review.id, hypothesis_type=htype
                ).count()
                Hypothesis.objects.create(
                    review_id=review.id,
                    statement=text,
                    hypothesis_type=htype,
                    order=order,
                )
                messages.success(request, "Hipótese adicionada.")
        elif action == "update_hypothesis":
            h = Hypothesis.objects.filter(
                id=request.POST.get("hypothesis_id"), review_id=review.id
            ).first()
            if h:
                text = (request.POST.get("statement") or "").strip()
                htype = request.POST.get("hypothesis_type", h.hypothesis_type)
                if htype in ("S", "W"):
                    h.hypothesis_type = htype
                if text:
                    h.statement = text
                h.save()
                messages.success(request, "Hipótese atualizada.")
        elif action == "delete_hypothesis":
            h = Hypothesis.objects.filter(
                id=request.POST.get("hypothesis_id"), review_id=review.id
            ).first()
            if h:
                h.delete()
                messages.success(request, "Hipótese removida.")
        elif action == "add_criteria":
            desc = (request.POST.get("description") or "").strip()
            ctype = request.POST.get("criteria_type", "I")
            if desc:
                SelectionCriteria.objects.create(
                    review_id=review.id, criteria_type=ctype, description=desc
                )
        elif action == "delete_criteria":
            c = SelectionCriteria.objects.filter(
                id=request.POST.get("criteria_id"), review_id=review.id
            ).first()
            if c:
                c.delete()
        elif action == "add_keyword":
            desc = (request.POST.get("keyword") or "").strip()
            related = request.POST.get("related_to", "P")
            synonym_of = request.POST.get("synonym_of_id")
            if desc:
                Keyword.objects.create(
                    review_id=review.id,
                    description=desc,
                    related_to=related,
                    synonym_of_id=int(synonym_of) if synonym_of else None,
                )
        elif action == "delete_keyword":
            kw = Keyword.objects.filter(
                id=request.POST.get("keyword_id"), review_id=review.id
            ).first()
            if kw:
                kw.delete()
        elif action == "copy_search_to_sources":
            n = copy_base_to_sources(review)
            if n:
                messages.success(request, f"String geral copiada para {n} base(s) de dados.")
            else:
                messages.warning(request, "Defina a string geral antes de copiar.")
        elif action == "update_sources":
            all_sources = Source.objects.order_by("name")
            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, "Bases de dados atualizadas.")
        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"Base «{name}» adicionada e selecionada.")
            else:
                messages.warning(request, "Informe o nome da base.")
        elif action == "save_source_strings":
            for source in review.sources.all():
                save_source_string(
                    review, source.id, request.POST.get(f"search_{source.id}", "")
                )
            messages.success(request, "Strings por base salvas.")
        return redirect(reverse("planning:protocol", kwargs={"review_id": review.id}))

    review.ensure_search_sessions()
    overview = get_search_overview(review)
    all_sources = Source.objects.order_by("name")
    selected_source_ids = set(review.sources.values_list("id", flat=True))
    root_keywords = Keyword.objects.filter(review_id=review.id, synonym_of_id__isnull=True)
    return render(
        request,
        "planning/protocol.html",
        {
            "review": review,
            "overview": overview,
            "all_sources": all_sources,
            "selected_source_ids": selected_source_ids,
            "root_keywords": root_keywords,
        },
    )


@login_required
@author_required
def quality(request, review):
    if request.method == "POST":
        action = request.POST.get("action")
        if action == "save_cutoff":
            try:
                review.quality_assessment_cutoff_score = float(
                    request.POST.get("cutoff_score") or 0
                )
                review.save()
                messages.success(request, "Pontuação de corte salva.")
            except ValueError:
                messages.error(request, "Pontuação de corte inválida.")
        elif action == "add_question":
            desc = (request.POST.get("description") or "").strip()
            if desc:
                QualityQuestion.objects.create(
                    review_id=review.id,
                    description=desc,
                    order=review.quality_questions.count(),
                )
        elif action == "update_question":
            q = QualityQuestion.objects.filter(
                id=request.POST.get("question_id"), review_id=review.id
            ).first()
            desc = (request.POST.get("description") or "").strip()
            if q and desc:
                q.description = desc
                q.save()
                messages.success(request, "Pergunta atualizada.")
        elif action == "delete_question":
            q = QualityQuestion.objects.filter(
                id=request.POST.get("question_id"), review_id=review.id
            ).first()
            if q:
                q.delete()
        elif action == "add_answer":
            desc = (request.POST.get("description") or "").strip()
            try:
                weight = float(request.POST.get("weight") or 0)
            except ValueError:
                weight = 0
            if desc:
                QualityAnswer.objects.create(
                    review_id=review.id,
                    description=desc,
                    weight=weight,
                    order=review.quality_answers.count(),
                )
        elif action == "update_answer":
            a = QualityAnswer.objects.filter(
                id=request.POST.get("answer_id"), review_id=review.id
            ).first()
            desc = (request.POST.get("description") or "").strip()
            try:
                weight = float(request.POST.get("weight") or 0)
            except ValueError:
                weight = a.weight if a else 0
            if a and desc:
                a.description = desc
                a.weight = weight
                a.save()
                messages.success(request, "Opção de resposta atualizada.")
        elif action == "delete_answer":
            a = QualityAnswer.objects.filter(
                id=request.POST.get("answer_id"), review_id=review.id
            ).first()
            if a:
                a.delete()
        return redirect(reverse("planning:quality", kwargs={"review_id": review.id}))

    return render(request, "planning/quality.html", {"review": review})


@login_required
@author_required
def extraction(request, review):
    from rsl.services.extraction_form import (
        EXTRACTION_FORM,
        FIELD_TYPE_LABELS,
        add_extraction_lookup,
        create_extraction_field,
        seed_extraction_form,
    )

    if request.method == "POST":
        action = request.POST.get("action")
        if action == "add_field":
            field, err = create_extraction_field(
                review,
                request.POST.get("description", ""),
                request.POST.get("field_type", "O"),
            )
            if err:
                messages.error(request, err)
            elif field:
                messages.success(request, f'Campo "{field.description}" adicionado.')
        elif action == "delete_field":
            field = DataExtractionField.objects.filter(
                id=request.POST.get("field_id"), review_id=review.id
            ).first()
            if field:
                field.delete()
        elif action == "add_lookup":
            lookup, err = add_extraction_lookup(
                review,
                int(request.POST.get("field_id") or 0),
                request.POST.get("value", ""),
            )
            if err:
                messages.error(request, err)
            elif lookup:
                messages.success(request, f'Opção "{lookup.value}" adicionada.')
        elif action == "delete_lookup":
            lookup = DataExtractionLookup.objects.filter(
                id=request.POST.get("lookup_id")
            ).first()
            if lookup and lookup.field.review_id == review.id:
                lookup.delete()
        elif action == "apply_default_form":
            n = seed_extraction_form(review, replace=True)
            messages.success(request, f"Formulário padrão aplicado ({n} campos).")
        elif action == "update_field":
            field = DataExtractionField.objects.filter(
                id=request.POST.get("field_id"), review_id=review.id
            ).first()
            desc = (request.POST.get("description") or "").strip()
            ftype = request.POST.get("field_type", "O")
            if field and desc:
                field.description = desc
                field.field_type = ftype
                field.save()
                messages.success(request, "Campo atualizado.")
        return redirect(reverse("planning:extraction", kwargs={"review_id": review.id}))

    return render(
        request,
        "planning/extraction.html",
        {
            "review": review,
            "default_form_size": len(EXTRACTION_FORM),
            "field_type_labels": FIELD_TYPE_LABELS,
            "extraction_field_types": list(DataExtractionField.FIELD_TYPES.items()),
        },
    )
