from http import HTTPStatus

from allauth.account.models import EmailAddress, get_emailconfirmation_model
from allauth.headless.constants import Flow


def test_auth_unverified_email_and_mfa(
    client,
    user_factory,
    password_factory,
    settings,
    totp_validation_bypass,
    headless_reverse,
    headless_client,
):
    settings.ACCOUNT_LOGIN_METHODS = {"email"}
    settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory"
    settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
    password = password_factory()
    user = user_factory(email_verified=False, password=password, with_totp=True)
    resp = client.post(
        headless_reverse("headless:account:login"),
        data={
            "email": user.email,
            "password": password,
        },
        content_type="application/json",
    )
    assert resp.status_code == HTTPStatus.UNAUTHORIZED
    data = resp.json()
    assert [f for f in data["data"]["flows"] if f["id"] == Flow.VERIFY_EMAIL][0][
        "is_pending"
    ]
    emailaddress = EmailAddress.objects.filter(user=user, verified=False).get()
    key = get_emailconfirmation_model().create(emailaddress).key
    resp = client.post(
        headless_reverse("headless:account:verify_email"),
        data={"key": key},
        content_type="application/json",
    )
    assert resp.status_code == HTTPStatus.UNAUTHORIZED
    flows = [
        {"id": "login"},
        {"id": "login_by_code"},
        {"id": "signup"},
    ]
    if headless_client == "browser":
        flows.append(
            {
                "id": "provider_redirect",
                "providers": ["dummy", "unittest-server", "other-server"],
            }
        )
    flows.append(
        {
            "id": "provider_token",
            "providers": ["dummy", "unittest-server", "other-server"],
        }
    )
    flows.append({"id": "mfa_login_webauthn"})
    flows.append(
        {
            "id": "mfa_authenticate",
            "is_pending": True,
            "types": ["totp"],
        }
    )

    assert resp.json() == {
        "data": {"flows": flows},
        "meta": {"is_authenticated": False},
        "status": HTTPStatus.UNAUTHORIZED,
    }
    resp = client.post(
        headless_reverse("headless:mfa:authenticate"),
        data={"code": "bad"},
        content_type="application/json",
    )
    assert resp.status_code == HTTPStatus.BAD_REQUEST
    assert resp.json() == {
        "status": HTTPStatus.BAD_REQUEST,
        "errors": [
            {"message": "Incorrect code.", "code": "incorrect_code", "param": "code"}
        ],
    }

    with totp_validation_bypass():
        resp = client.post(
            headless_reverse("headless:mfa:authenticate"),
            data={"code": "bad"},
            content_type="application/json",
        )
    assert resp.status_code == HTTPStatus.OK


def test_dangling_mfa_is_logged_out(
    client,
    user_with_totp,
    password_factory,
    settings,
    totp_validation_bypass,
    headless_reverse,
    headless_client,
    user_password,
):
    settings.ACCOUNT_LOGIN_METHODS = {"email"}
    resp = client.post(
        headless_reverse("headless:account:login"),
        data={
            "email": user_with_totp.email,
            "password": user_password,
        },
        content_type="application/json",
    )
    assert resp.status_code == HTTPStatus.UNAUTHORIZED
    data = resp.json()
    flow = [f for f in data["data"]["flows"] if f["id"] == Flow.MFA_AUTHENTICATE][0]
    assert flow["is_pending"]
    assert flow["types"] == ["totp"]
    resp = client.delete(headless_reverse("headless:account:current_session"))
    data = resp.json()
    assert resp.status_code == HTTPStatus.UNAUTHORIZED
    assert all(not f.get("is_pending") for f in data["data"]["flows"])
