import time
from functools import partial

from clarabot_nano_common.common.protocol.errors import (
    NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING,
    NANO_REQUEST_ERROR_TOO_MANY_FAILURES,
)

from clarabot_nano.common.config.extract.admin_password import AdminPasswordExtractedConfig
from clarabot_nano.gateway.state.admin import GatewayAdminSessions, AdminAuthenticationError


def test_gateway_state_admin_fails(monkeypatch):
    conf = [
        AdminPasswordExtractedConfig({"local": {"remote_admin_policy": "restrict"}, "remote": {"admin_password": None}})
    ]
    gas = GatewayAdminSessions(lambda: conf[0])

    def _create_error(gas_, pw_, err_):
        try:
            gas_.create_session(pw_)
        except AdminAuthenticationError as e:
            assert e.code == err_
        else:
            assert False

    def _validate_error(gas_, sess_, err_):
        try:
            gas_.validate_session(sess_)
        except AdminAuthenticationError as e:
            assert e.code == err_
        else:
            assert False

    _perf_counter = time.perf_counter

    def _perf_counter_x(delta):
        return _perf_counter() + delta

    with monkeypatch.context() as m:
        for i in range(8):
            if i == 2:
                m.setattr("time.perf_counter", partial(_perf_counter_x, 20))
            _validate_error(gas, "asdf", NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING)
        m.setattr("time.perf_counter", partial(_perf_counter_x, 40))
        _validate_error(gas, "asdf", NANO_REQUEST_ERROR_TOO_MANY_FAILURES)
        m.setattr("time.perf_counter", partial(_perf_counter_x, 60))
        for i in range(2):
            _validate_error(gas, "asdf", NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING)
        _validate_error(gas, "asdf", NANO_REQUEST_ERROR_TOO_MANY_FAILURES)

    # None does not count to fails
    for i in range(15):
        _create_error(gas, None, NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING)
    with monkeypatch.context() as m:
        for i in range(5):
            if i == 2:
                m.setattr("time.perf_counter", partial(_perf_counter_x, 20))
            _create_error(gas, "asdf", NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING)
        m.setattr("time.perf_counter", partial(_perf_counter_x, 40))
        _create_error(gas, "asdf", NANO_REQUEST_ERROR_TOO_MANY_FAILURES)
        m.setattr("time.perf_counter", partial(_perf_counter_x, 60))
        for i in range(2):
            _create_error(gas, "asdf", NANO_REQUEST_ERROR_ADMIN_PASSWORD_MISSING)
        _create_error(gas, "asdf", NANO_REQUEST_ERROR_TOO_MANY_FAILURES)
