"""testing auth things"""

import logging
import os

import pytest
from pytest_mock import MockerFixture

# pylint: disable=unused-import
from .testutils import client, client_configfile, MockResponse

from kanidm import KanidmClient
from kanidm.exceptions import AuthCredFailed, AuthInitFailed
from kanidm.types import AuthBeginResponse
from kanidm.tokens import TokenStore


logging.basicConfig(level=logging.DEBUG)


@pytest.mark.network
@pytest.mark.asyncio
async def test_auth_init(client_configfile: KanidmClient) -> None:
    """tests the auth init step"""
    print("Starting client...")
    print(f"Doing auth_init for {client_configfile.config.username}")

    username = client_configfile.config.username

    if username is None:
        raise pytest.skip("Can't run auth test without a username/password")
    result = await client_configfile.auth_init(username)
    print(f"{result=}")
    print(result.model_dump_json())
    assert result.sessionid


@pytest.mark.network
@pytest.mark.asyncio
async def test_auth_begin(client_configfile: KanidmClient) -> None:
    """tests the auth begin step"""
    print(f"Doing auth_init for {client_configfile.config.username}")

    username = client_configfile.config.username

    if username is None:
        raise pytest.skip("Can't run auth test without a username/password")  # type: ignore[call-non-callable]
    result = await client_configfile.auth_init(username)
    print(f"{result=}")
    print("Result dict:")
    print(result.model_dump_json())
    assert result.sessionid

    print(f"Doing auth_begin for {client_configfile.config.username}")
    if result.response is None:
        raise ValueError("Failed to get response")
    sessionid = result.response.headers["x-kanidm-auth-session-id"]
    begin_result = await client_configfile.auth_begin(
        sessionid=sessionid,
        method="password",
    )
    print(f"{begin_result=}")
    print(begin_result.data)
    retval = begin_result.data

    if retval is None:
        raise pytest.fail("Failed to do begin_result")  # type: ignore[call-non-callable]

    retval["response"] = begin_result.model_dump()

    assert AuthBeginResponse.model_validate(retval)


@pytest.mark.network
@pytest.mark.asyncio
async def test_authenticate_flow(client_configfile: KanidmClient) -> None:
    """tests the authenticate() flow"""
    if client_configfile.config.username is None or client_configfile.config.password is None:
        pytest.skip("Can't run this without a username and password set in the config file")  # type: ignore[call-non-callable]

    client_configfile.config.auth_token = None
    print(f"Doing client.authenticate for {client_configfile.config.username}")
    result = await client_configfile.authenticate_password()
    print(result)


@pytest.mark.network
@pytest.mark.asyncio
async def test_authenticate_anonymous(client_configfile: KanidmClient) -> None:
    """tests the authenticate() flow"""

    client_configfile.config.auth_token = None
    print("Doing anonymous auth")
    await client_configfile.auth_as_anonymous()
    assert client_configfile.config.auth_token is not None


@pytest.mark.network
@pytest.mark.asyncio
async def test_authenticate_flow_fail(client_configfile: KanidmClient) -> None:
    """tests the authenticate() flow with a valid (hopefully) username and invalid password"""
    if not bool(os.getenv("RUN_SCARY_TESTS", None)):
        pytest.skip("Skipping because env var RUN_SCARY_TESTS isn't set")  # type: ignore[call-non-callable]
    print("Starting client...")
    if client_configfile.config.uri is None or client_configfile.config.username is None or client_configfile.config.password is None:
        pytest.skip("Please ensure you have a username, password and uri in the config")  # type: ignore[call-non-callable]
    print(f"Doing client.authenticate for {client_configfile.config.username}")

    client_configfile.config.auth_token = None

    with pytest.raises((AuthCredFailed, AuthInitFailed)):
        result = await client_configfile.authenticate_password(
            username=client_configfile.config.username,
            password="cheese",
        )
        print(result)


# TODO: mock a call to auth_init when a 200 response is not returned, raises AuthInitFailed
# TODO: mock a call to auth_init when "x-kanidm-auth-session-id" not in response.headers, raises ValueError


# TODO: mock a call to auth_begin when a 200 response is not returned, raises AuthBeginFailed
# TODO: mock a call to auth_step_password when a 200 response is not returned, raises AuthCredFailed


@pytest.mark.asyncio
async def test_authenticate_inputs_validation(client: KanidmClient, mocker: MockerFixture) -> None:
    """tests if you pass username but not password and password but not username"""

    resp = MockResponse("crabs are cool", 200)

    mocker.patch("aiohttp.ClientSession.post", return_value=resp)

    with pytest.raises(ValueError):
        await client.authenticate_password(username="cheese")
    with pytest.raises(ValueError):
        await client.authenticate_password(password="cheese")
    client.config.password = None
    client.config.username = "crabby"
    with pytest.raises(ValueError):
        await client.authenticate_password()
    client.config.password = "cR4bzR0ol"
    client.config.username = None
    with pytest.raises(ValueError):
        await client.authenticate_password()

    client.config.username = None
    client.config.password = None
    with pytest.raises(ValueError):
        await client.authenticate_password()


@pytest.mark.network
@pytest.mark.asyncio
async def test_auth_step_password(client: KanidmClient) -> None:
    """tests things"""

    with pytest.raises(ValueError):
        await client.auth_step_password(sessionid="asdf")


@pytest.mark.network
@pytest.mark.asyncio
async def test_authenticate_with_token(client_configfile: KanidmClient) -> None:
    """tests auth with a token, needs to have a valid token in your local cache"""

    logging.basicConfig(level=logging.DEBUG)

    if "KANIDM_TEST_USERNAME" in os.environ:
        test_username: str = os.environ["KANIDM_TEST_USERNAME"]
        print(f"Using username {test_username} from KANIDM_TEST_USERNAME env var")
    else:
        test_username = "idm_admin"
        print(f"Using username {test_username} by default - set KANIDM_TEST_USERNAME env var if you want to change this.")

    tokens = TokenStore.model_validate({})
    tokens.load()
