import logging

from django.core.exceptions import ObjectDoesNotExist
from django.http import FileResponse, Http404, HttpResponse
from django.utils import timezone
from django.utils.dateparse import parse_date
from django.utils.decorators import method_decorator
from django_ratelimit.decorators import ratelimit
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from portfolio.views_profile import HasStudentProfile, _profile_for

from .generation import (
    build_resume_context,
    generate_docx_from_html,
    render_resume_html,
    save_generated_files,
)
from .keyword_extractor import extract_keywords
from .matcher import match_with_portfolio
from .models import GeneratedResume, ResumeAnalytics, ResumeTemplate
from .serializers import (
    GeneratedResumeListSerializer,
    ResumeGenerateSerializer,
    ResumePreviewSerializer,
    VacancyAnalyzeSerializer,
)

logger = logging.getLogger(__name__)


@method_decorator(ratelimit(key="ip", rate="40/m", method="POST"), name="dispatch")
class ResumeAnalyzeView(APIView):
    permission_classes = (permissions.IsAuthenticated, HasStudentProfile)

    def post(self, request):
        ser = VacancyAnalyzeSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        text = ser.validated_data["vacancy_text"]
        keywords = extract_keywords(text)
        profile = _profile_for(request)
        match_result = match_with_portfolio(profile, keywords)
        return Response(
            {
                "keywords": keywords,
                "matched_skills": [
                    {"id": s.id, "name": s.name, "level": s.level}
                    for s in match_result["skills"]
                ],
                "matched_projects": [
                    {"id": p.id, "title": p.title} for p in match_result["projects"]
                ],
                "has_match": match_result["has_match"],
            }
        )


@method_decorator(ratelimit(key="user", rate="20/m", method="POST"), name="dispatch")
class ResumePreviewView(APIView):
    """Живое превью без сохранения в БД."""

    permission_classes = (permissions.IsAuthenticated, HasStudentProfile)

    def post(self, request):
        ser = ResumePreviewSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        text = ser.validated_data["vacancy_text"]
        template_id = ser.validated_data["template_id"]
        try:
            tpl = ResumeTemplate.objects.get(pk=template_id, is_active=True)
        except ResumeTemplate.DoesNotExist:
            return Response(
                {"detail": "Шаблон не найден."},
                status=status.HTTP_400_BAD_REQUEST,
            )
        profile = _profile_for(request)
        keywords = extract_keywords(text)
        match_result = match_with_portfolio(profile, keywords)
        ctx = build_resume_context(profile, keywords, match_result, request=request)
        html = render_resume_html(tpl, ctx)
        return Response(
            {
                "html_content": html,
                "keywords": keywords,
                "matched_skills": [
                    {"id": s.id, "name": s.name, "level": s.level}
                    for s in match_result["skills"]
                ],
                "matched_projects": [
                    {"id": p.id, "title": p.title} for p in match_result["projects"]
                ],
            }
        )


@method_decorator(ratelimit(key="user", rate="15/m", method="POST"), name="dispatch")
class ResumeGenerateView(APIView):
    permission_classes = (permissions.IsAuthenticated, HasStudentProfile)

    def post(self, request):
        ser = ResumeGenerateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        text = ser.validated_data["vacancy_text"]
        template_id = ser.validated_data["template_id"]
        is_public = ser.validated_data.get("is_public", False)

        try:
            tpl = ResumeTemplate.objects.get(pk=template_id, is_active=True)
        except ResumeTemplate.DoesNotExist:
            return Response(
                {"detail": "Шаблон не найден."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        profile = _profile_for(request)
        keywords = extract_keywords(text)
        match_result = match_with_portfolio(profile, keywords)
        ctx = build_resume_context(profile, keywords, match_result, request=request)
        logger.info(
            "Резюме: ключи контекста=%s; ФИО=%s; навыков=%s; проектов=%s",
            list(ctx.keys()),
            (ctx.get("student") or {}).get("full_name"),
            len(ctx.get("skills") or []),
            len(ctx.get("projects") or []),
        )

        selected_data = {
            "keywords": keywords,
            "matched_skill_ids": [s.id for s in match_result["skills"]],
            "matched_project_ids": [p.id for p in match_result["projects"]],
        }

        gr = GeneratedResume.objects.create(
            student=profile,
            template=tpl,
            vacancy_text=text,
            vacancy_keywords=keywords,
            selected_data=selected_data,
            is_public=is_public,
        )

        html = render_resume_html(tpl, ctx)
        base_url = request.build_absolute_uri("/")
        pdf_err = save_generated_files(gr, html, ctx, base_url=base_url)
        gr.refresh_from_db()

        warn_pdf = not bool(gr.pdf_file)
        warn_msg = None
        if warn_pdf:
            warn_msg = pdf_err or (
                "PDF не создан (WeasyPrint и резервный xhtml2pdf недоступны). "
                "См. docs/WINDOWS_PDF_SETUP.md. Доступны HTML и DOCX."
            )
        docx_ok = bool(gr.docx_file)
        return Response(
            {
                "id": gr.id,
                "html_content": gr.html_content,
                "keywords": keywords,
                "pdf_url": gr.pdf_file.url if gr.pdf_file else "",
                "docx_url": gr.docx_file.url if gr.docx_file else "",
                "pdf_available": not warn_pdf,
                "docx_available": docx_ok,
                "warning": warn_msg,
            },
            status=status.HTTP_201_CREATED,
        )


class ResumeHistoryView(APIView):
    permission_classes = (permissions.IsAuthenticated, HasStudentProfile)

    def get(self, request):
        qs = _profile_for(request).generated_resumes.all()
        df = request.query_params.get("date_from")
        dt = request.query_params.get("date_to")
        if df:
            d = parse_date(df)
            if d:
                qs = qs.filter(created_at__date__gte=d)
        if dt:
            d2 = parse_date(dt)
            if d2:
                qs = qs.filter(created_at__date__lte=d2)
        qs = qs.select_related("template", "analytics").order_by("-created_at")
        data = []
        for gr in qs:
            item = GeneratedResumeListSerializer(gr).data
            try:
                an = gr.analytics
                item["views_count"] = an.views_count
                item["downloads_count"] = an.downloads_count
                item["last_viewed"] = an.last_viewed
            except ObjectDoesNotExist:
                item["views_count"] = 0
                item["downloads_count"] = 0
                item["last_viewed"] = None
            data.append(item)
        return Response(data)


def _get_resume_for_user(user, pk):
    try:
        gr = GeneratedResume.objects.select_related("student__user").get(pk=pk)
    except GeneratedResume.DoesNotExist:
        raise Http404()
    if gr.student.user_id != user.id:
        raise Http404()
    return gr


class ResumeDetailView(APIView):
    """Просмотр HTML резюме + учёт просмотра (владелец)."""

    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, pk):
        gr = _get_resume_for_user(request.user, pk)
        if gr.student.user_id == request.user.id:
            an, _ = ResumeAnalytics.objects.get_or_create(resume=gr)
            an.views_count += 1
            an.last_viewed = timezone.now()
            an.save(update_fields=["views_count", "last_viewed"])
        return Response(
            {
                "id": gr.id,
                "html_content": gr.html_content,
                "created_at": gr.created_at,
            }
        )


class ResumeExportPdfView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, pk):
        gr = _get_resume_for_user(request.user, pk)
        if not gr.pdf_file or not gr.pdf_file.name:
            path_html = f"/api/resume/{gr.pk}/export/html/"
            return Response(
                {
                    "detail": "PDF не создан",
                    "pdf_available": False,
                    "html_available": bool((gr.html_content or "").strip()),
                    "export_html_url": request.build_absolute_uri(path_html),
                },
                status=status.HTTP_404_NOT_FOUND,
            )
        an, _ = ResumeAnalytics.objects.get_or_create(resume=gr)
        an.downloads_count += 1
        an.save(update_fields=["downloads_count"])
        return FileResponse(
            gr.pdf_file.open("rb"),
            as_attachment=True,
            filename=f"resume_{gr.id}.pdf",
            content_type="application/pdf",
        )


class ResumeExportHtmlView(APIView):
    """Скачивание резюме как HTML (fallback при отсутствии PDF)."""

    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, pk):
        gr = _get_resume_for_user(request.user, pk)
        if not (gr.html_content or "").strip():
            return Response(
                {"detail": "HTML контент не найден"},
                status=status.HTTP_404_NOT_FOUND,
            )
        resp = HttpResponse(
            gr.html_content, content_type="text/html; charset=utf-8"
        )
        resp["Content-Disposition"] = f'attachment; filename="resume_{gr.id}.html"'
        an, _ = ResumeAnalytics.objects.get_or_create(resume=gr)
        an.downloads_count += 1
        an.save(update_fields=["downloads_count"])
        return resp


class ResumeExportDocxView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, pk):
        gr = _get_resume_for_user(request.user, pk)
        an, _ = ResumeAnalytics.objects.get_or_create(resume=gr)
        if gr.docx_file and gr.docx_file.name:
            an.downloads_count += 1
            an.save(update_fields=["downloads_count"])
            return FileResponse(
                gr.docx_file.open("rb"),
                as_attachment=True,
                filename=f"resume_{gr.id}.docx",
                content_type=(
                    "application/vnd.openxmlformats-officedocument."
                    "wordprocessingml.document"
                ),
            )
        raw = (gr.html_content or "").strip()
        if raw:
            docx_bytes = generate_docx_from_html(raw)
            if docx_bytes:
                an.downloads_count += 1
                an.save(update_fields=["downloads_count"])
                resp = HttpResponse(
                    docx_bytes,
                    content_type=(
                        "application/vnd.openxmlformats-officedocument."
                        "wordprocessingml.document"
                    ),
                )
                resp["Content-Disposition"] = (
                    f'attachment; filename="resume_{gr.id}.docx"'
                )
                return resp
        path_html = f"/api/resume/{gr.pk}/export/html/"
        return Response(
            {
                "detail": "DOCX не создан",
                "docx_available": False,
                "html_available": bool((gr.html_content or "").strip()),
                "export_html_url": request.build_absolute_uri(path_html),
            },
            status=status.HTTP_404_NOT_FOUND,
        )
