"""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}")

    if client_configfile.config.username is None:
        pytest.skip("Can't run auth test without a username/password")
    result = await client_configfile.auth_init(client_configfile.config.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}")

    if client_configfile.config.username is None:
        pytest.skip("Can't run auth test without a username/password")
    result = await client_configfile.auth_init(client_configfile.config.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")

    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")

    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(reason="Skipping because env var RUN_SCARY_TESTS isn't set")
    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")
    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()

    # TODO: make this actually work now instances are a thing

    # if test_username not in tokens:
    #     print(f"Can't find {test_username} user in token store")
    #     raise pytest.skip(f"Can't find {test_username} user in token store")
    # test_token: str = tokens[test_username]
    # if not await client_configfile.check_token_valid(test_token):
    #     print(f"Token for {test_username} isn't valid")
    #     pytest.skip(f"Token for {test_username} isn't valid")
    # else:
    #     print("Token was noted as valid, so auth works!")

    # # tests the "we set a token and well it works."
    # client_configfile.config.auth_token = tokens[test_username]
    # result = await client_configfile.call_get("/v1/self")
    # print(result)

    # assert result.status_code == 200
