import datetime
import logging

import time_machine

from canaille.backends.ldap.backend import LDAPBackend
from canaille.core.models import PASSWORD_MIN_DELAY


def test_intruder_lockout_fail_second_attempt_then_succeed_in_third(
    testclient, user, caplog
):
    testclient.app.config["CANAILLE"]["ENABLE_INTRUDER_LOCKOUT"] = True

    with testclient.session_transaction() as session:
        assert not session.get("user_id")

    # add 500 milliseconds to account for LDAP time
    with time_machine.travel(
        datetime.datetime.now(datetime.timezone.utc)
        + datetime.timedelta(milliseconds=500),
        tick=False,
    ) as traveller:
        res = testclient.get("/login", status=200)

        res.form["login"] = "user"
        res = res.form.submit(status=302).follow()

        res.form["password"] = "incorrect horse"
        res = res.form.submit(status=200)

        assert ("error", "Login failed, please check your information") in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        res.form["password"] = "correct horse battery staple"
        res = res.form.submit(status=200)

        assert (
            "error",
            f"Too much attempts. Please wait for {PASSWORD_MIN_DELAY} seconds before trying to login again.",
        ) in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        traveller.shift(datetime.timedelta(seconds=PASSWORD_MIN_DELAY))

        res.form["password"] = "correct horse battery staple"
        res = res.form.submit(status=302)

        assert (
            "success",
            "Connection successful. Welcome John (johnny) Doe",
        ) in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Succeed login attempt for user",
        ) in caplog.record_tuples


def test_intruder_lockout_two_consecutive_fails(testclient, backend, user, caplog):
    testclient.app.config["CANAILLE"]["ENABLE_INTRUDER_LOCKOUT"] = True

    # add 500 milliseconds to account for LDAP time
    with time_machine.travel(
        datetime.datetime.now(datetime.timezone.utc)
        + datetime.timedelta(milliseconds=500),
        tick=False,
    ) as traveller:
        res = testclient.get("/login", status=200)

        res.form["login"] = "user"
        res = res.form.submit().follow()

        res.form["password"] = "incorrect horse"
        res = res.form.submit(status=200)

        assert ("error", "Login failed, please check your information") in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        res.form["password"] = "correct horse battery staple"
        res = res.form.submit(status=200)

        assert (
            "error",
            f"Too much attempts. Please wait for {PASSWORD_MIN_DELAY} seconds before trying to login again.",
        ) in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        traveller.shift(datetime.timedelta(seconds=PASSWORD_MIN_DELAY))
        ldap_shift = (
            PASSWORD_MIN_DELAY if isinstance(backend, LDAPBackend) else 0
        )  # LDAP doesn't travel in time!

        res.form["password"] = "incorrect horse"
        res = res.form.submit(status=200)

        assert ("error", "Login failed, please check your information") in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        res.form["password"] = "correct horse battery staple"
        res = res.form.submit(status=200)

        assert (
            "error",
            f"Too much attempts. Please wait for {PASSWORD_MIN_DELAY * 2 - ldap_shift} seconds before trying to login again.",
        ) in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Failed login attempt for user",
        ) in caplog.record_tuples

        traveller.shift(datetime.timedelta(seconds=PASSWORD_MIN_DELAY * 2))

        res.form["password"] = "correct horse battery staple"
        res = res.form.submit(status=302)

        assert (
            "success",
            "Connection successful. Welcome John (johnny) Doe",
        ) in res.flashes
        assert (
            "canaille",
            logging.SECURITY,
            "Succeed login attempt for user",
        ) in caplog.record_tuples
