import textwrap
from typing import final

from django import forms
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
from django.shortcuts import render
from typing_extensions import override

from vuln_demo.apps.vulnerabilities.levels.abc import LevelABC
from vuln_demo.apps.vulnerabilities.models import CompletedLevel
from vuln_demo.util.auth import cast_user
from vuln_demo.util.pw_entries import ADMIN_EMAIL, USER_EMAIL

COOKIE_NAME = "level3_userId"


class PasswordResetForm(forms.Form):
    """Form class passed to level 2 view."""

    new_password = forms.CharField(
        label="New Password",
        widget=forms.PasswordInput(attrs={"autofocus": "autofocus"}),
    )


@final
class Level3(LevelABC):
    """Scenario with sensitive data in hidden unsigned cookies."""

    HINT_AMOUNT = 3

    @override
    @classmethod
    def _get_hint(cls, request: WSGIRequest, hint_num: int) -> str:
        if hint_num == 1:
            return textwrap.dedent(
                """
                <p>
                    As you've learned in the previous level, each request across the web is taken anew, and so
                    these requests have to somehow carry all of the information needed. Thankfully, the browser
                    is a handy tool, and can track all of the data sent out for us.
                </p>
                <p>
                    The information in the request can be carried in various ways, and unlike in the level before,
                    this one might be a bit trickier to uncover. If you look deep enough though, you just might strike
                    gold.
                </p>
            """
            )
        if hint_num == 2:
            return textwrap.dedent(
                """
                <p>
                    When you open the Developer's Tools in your browser, go to Network. You can find all of the
                    requests that your browser has made. Submit the password reset form and see what requests were
                    made, and what information hides in them.
                </p>
                """
            )
        if hint_num == 3:
            return textwrap.dedent(
                """
                <p>
                    A common way for web developers to keep track of the session is via cookies. Check what cookie is
                    being sent over with your requests. Can you find a way to modify this cookie, and trick the server
                    into thinking you're someone else?
                </p>
                """
            )
        raise

    @override
    def get(self, request: WSGIRequest) -> HttpResponse:
        assert cast_user(request.user, cast=True)
        already_completed = CompletedLevel.check_completed(request.user, 3)

        form = PasswordResetForm()
        response = render(
            request,
            "vulnerabilities/levels/level3.html",
            {
                "form": form,
                "completed": already_completed,
            },
        )
        # Set a cookie with userid, containing the user email, this is what the
        # user will need to change to get in
        response.set_cookie(
            COOKIE_NAME,
            USER_EMAIL,
            max_age=86400,
            samesite="Strict",
            path=request.path,
        )
        return response

    def post(self, request: WSGIRequest) -> HttpResponse:
        """Handle password form submission."""
        assert cast_user(request.user, cast=True)
        already_completed = CompletedLevel.check_completed(request.user, 3)

        form = PasswordResetForm(request.POST)
        if not form.is_valid():
            return render(
                request,
                "vulnerabilities/levels/level3.html",
                {"form": form, "completed": already_completed},
            )

        email = request.COOKIES.get(COOKIE_NAME)
        if email is None:
            response = render(
                request,
                "vulnerabilities/levels/level3.html",
                {
                    "form": form,
                    "completed": already_completed,
                    "error_msg": (
                        "CyberVault returned a 401 - Unauthorized error (You seem to have deleted the "
                        f"{COOKIE_NAME} cookie, we resent a new cookie for you, re-authorizing you to CyberVault.).",
                    ),
                },
            )
            response.set_cookie(
                COOKIE_NAME,
                USER_EMAIL,
                max_age=86400,
                samesite="Strict",
                path=request.path,
            )
            return response

        if email == USER_EMAIL:
            return render(
                request,
                "vulnerabilities/levels/level3.html",
                {
                    "form": form,
                    "completed": already_completed,
                    "note_msg": "Your CyberVault password was changed (but you still can't reach the admin account)",
                },
            )

        if email != ADMIN_EMAIL:
            return render(
                request,
                "vulnerabilities/levels/level3.html",
                {
                    "form": form,
                    "completed": already_completed,
                    "error_msg": "No such user (you're close though)",
                },
            )

        return self.completed_redirect(request, 3, skip_db=already_completed)
