"""View mixins for ECP Auth.

Provides mixins to integrate ECP authentication into existing class-based views.
"""

import logging
from typing import Any

from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import redirect

from ecp_auth.crypto import generate_user_certificate
from ecp_auth.exceptions import ECPAuthError
from ecp_auth.forms import ECPLoginForm
from ecp_auth.models import UserCertificate
from ecp_auth.parsers import extract_certificate_info
from ecp_auth.validators import validate_certificate

logger = logging.getLogger(__name__)


class ECPRegistrationViewMixin:
    """Mixin to integrate ECP certificate linking into a host project's RegisterView.

    This mixin overrides ``form_valid`` to intercept the successful creation
    of a user (assuming ``self.object`` is populated by ``CreateView``).
    If an ECP certificate was uploaded in the form, it parses, validates, and
    links the certificate to the newly created user.
    """

    def form_valid(self, form: Any) -> HttpResponse:
        """Call the parent's form_valid and attempt to link the certificate.

        Args:
            form: The validated registration form containing ECP fields.

        Returns:
            The HTTP response from the parent view.
        """
        # Let the parent view create the user
        response = super().form_valid(form)

        # Retrieve the newly created user
        user = getattr(self, "object", None)
        if not user:
            logger.warning("ECPRegistrationViewMixin requires self.object to be set by the parent view.")
            return response

        # Check if the user opted to generate an ECP key
        generate_ecp = form.cleaned_data.get("generate_ecp")
        if not generate_ecp:
            return response

        ecp_password = form.cleaned_data.get("ecp_password", "")

        try:
            p12_bytes, certificate = generate_user_certificate(
                user_identifier=getattr(user, "username", str(user.pk)),
                password=ecp_password,
            )

            cert_info = validate_certificate(certificate)

            # Check if certificate is already registered (unlikely since it's freshly generated)
            if UserCertificate.objects.filter(
                fingerprint_sha256=cert_info.fingerprint_sha256
            ).exists():
                messages.warning(
                    self.request,
                    "Акаунт створено, але виникла колізія сертифікату."
                )
                return response

            # Link the certificate to the new user
            UserCertificate.objects.create(
                user=user,
                fingerprint_sha256=cert_info.fingerprint_sha256,
                subject_dn=cert_info.subject_dn,
                issuer_dn=cert_info.issuer_dn,
                serial_number=cert_info.serial_number,
                common_name=cert_info.common_name,
                organization=cert_info.organization,
                not_valid_before=cert_info.not_valid_before,
                not_valid_after=cert_info.not_valid_after,
                certificate_pem=cert_info.pem_data,
                is_active=True,
            )

            # Store in session for download
            self.request.session["ecp_download_p12"] = p12_bytes.hex()
            # Store the original success_url to redirect the user back to the normal flow after download
            self.request.session["ecp_next_url"] = str(getattr(self, "success_url", "/"))

            # Redirect to the download page
            return redirect("ecp_auth:download_cert_page")

        except Exception as exc:
            logger.warning("Failed to link ECP certificate during registration: %s", str(exc))
            messages.warning(
                self.request,
                f"Помилка при збереженні сертифікату: {str(exc)}"
            )


        return response


class ECPLoginViewMixin:
    """Mixin to integrate ECP certification login into a host project's LoginView.
    
    This mixin sets the ``form_class`` to ``ECPLoginForm`` and overrides
    ``form_valid`` to ensure ECP session data is persisted if the user
    was authenticated via a certificate.
    """
    form_class = ECPLoginForm

    def form_valid(self, form: Any) -> HttpResponse:
        """Call the parent's form_valid and persist any ECP session parameters."""
        response = super().form_valid(form) # type: ignore[misc]
        
        # If the user logged in with ECP, the form attached params to the request
        if hasattr(self.request, "ecp_login_params"): # type: ignore[attr-defined]
            params = self.request.ecp_login_params # type: ignore[attr-defined]
            self.request.session["certificate_fingerprint"] = params["login_fingerprint"]
            self.request.session["certificate_cn"] = params["certificate_cn"]
            
        return response
