# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=missing-function-docstring
import datetime

from tests.common import generate_api_query, mnemonic_to_hex, read_json, write_json

TOKENS_FILE_CONTETS = {
    "tokens": [
        {
            "token": "TEST_TOKEN",
            "name": "test_token",
            "date": "2022-01-14 08:31:10.789314",
        },
        {
            "token": "TEST_TOKEN2",
            "name": "test_token2",
            "date": "2022-01-14 08:31:10.789314",
        },
    ]
}

API_RECOVERY_QUERY = """
recoveryKey {
    exists
    valid
    creationDate
    expirationDate
    usesLeft
}
"""


def test_graphql_recovery_key_status_unauthorized(client, tokens_file):
    response = client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is None


def test_graphql_recovery_key_status_when_none_exists(authorized_client, tokens_file):
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is False
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is False
    assert response.json()["data"]["api"]["recoveryKey"]["creationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["expirationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] is None


API_RECOVERY_KEY_GENERATE_MUTATION = """
mutation TestGenerateRecoveryKey($limits: RecoveryKeyLimitsInput) {
    getNewRecoveryApiKey(limits: $limits) {
        success
        message
        code
        key
    }
}
"""

API_RECOVERY_KEY_USE_MUTATION = """
mutation TestUseRecoveryKey($input: UseRecoveryKeyInput!) {
    useRecoveryApiKey(input: $input) {
        success
        message
        code
        token
    }
}
"""


def test_graphql_generate_recovery_key(client, authorized_client, tokens_file):
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is not None
    assert (
        response.json()["data"]["getNewRecoveryApiKey"]["key"].split(" ").__len__()
        == 18
    )
    assert read_json(tokens_file)["recovery_token"] is not None
    time_generated = read_json(tokens_file)["recovery_token"]["date"]
    assert time_generated is not None
    key = response.json()["data"]["getNewRecoveryApiKey"]["key"]
    assert (
        datetime.datetime.strptime(time_generated, "%Y-%m-%dT%H:%M:%S.%f")
        - datetime.timedelta(seconds=5)
        < datetime.datetime.now()
    )

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is True
    assert response.json()["data"]["api"]["recoveryKey"][
        "creationDate"
    ] == time_generated.replace("Z", "")
    assert response.json()["data"]["api"]["recoveryKey"]["expirationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] is None

    # Try to use token
    response = client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": key,
                    "deviceName": "new_test_token",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None
    assert (
        response.json()["data"]["useRecoveryApiKey"]["token"]
        == read_json(tokens_file)["tokens"][2]["token"]
    )
    assert read_json(tokens_file)["tokens"][2]["name"] == "new_test_token"

    # Try to use token again
    response = client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": key,
                    "deviceName": "new_test_token2",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None
    assert (
        response.json()["data"]["useRecoveryApiKey"]["token"]
        == read_json(tokens_file)["tokens"][3]["token"]
    )
    assert read_json(tokens_file)["tokens"][3]["name"] == "new_test_token2"


def test_graphql_generate_recovery_key_with_expiration_date(
    client, authorized_client, tokens_file
):
    expiration_date = datetime.datetime.now() + datetime.timedelta(minutes=5)
    expiration_date_str = expiration_date.strftime("%Y-%m-%dT%H:%M:%S.%f")
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "expirationDate": expiration_date_str,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is not None
    assert (
        response.json()["data"]["getNewRecoveryApiKey"]["key"].split(" ").__len__()
        == 18
    )
    assert read_json(tokens_file)["recovery_token"] is not None

    key = response.json()["data"]["getNewRecoveryApiKey"]["key"]
    assert read_json(tokens_file)["recovery_token"]["expiration"] == expiration_date_str
    assert read_json(tokens_file)["recovery_token"]["token"] == mnemonic_to_hex(key)

    time_generated = read_json(tokens_file)["recovery_token"]["date"]
    assert time_generated is not None
    assert (
        datetime.datetime.strptime(time_generated, "%Y-%m-%dT%H:%M:%S.%f")
        - datetime.timedelta(seconds=5)
        < datetime.datetime.now()
    )

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is True
    assert response.json()["data"]["api"]["recoveryKey"][
        "creationDate"
    ] == time_generated.replace("Z", "")
    assert (
        response.json()["data"]["api"]["recoveryKey"]["expirationDate"]
        == expiration_date_str
    )
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] is None

    # Try to use token
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": key,
                    "deviceName": "new_test_token",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None
    assert (
        response.json()["data"]["useRecoveryApiKey"]["token"]
        == read_json(tokens_file)["tokens"][2]["token"]
    )

    # Try to use token again
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": key,
                    "deviceName": "new_test_token2",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None
    assert (
        response.json()["data"]["useRecoveryApiKey"]["token"]
        == read_json(tokens_file)["tokens"][3]["token"]
    )

    # Try to use token after expiration date
    new_data = read_json(tokens_file)
    new_data["recovery_token"]["expiration"] = (
        datetime.datetime.now() - datetime.timedelta(minutes=5)
    ).strftime("%Y-%m-%dT%H:%M:%S.%f")
    write_json(tokens_file, new_data)
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": key,
                    "deviceName": "new_test_token3",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is False
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 404
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is None

    assert read_json(tokens_file)["tokens"] == new_data["tokens"]

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is False
    assert (
        response.json()["data"]["api"]["recoveryKey"]["creationDate"] == time_generated
    )
    assert (
        response.json()["data"]["api"]["recoveryKey"]["expirationDate"]
        == new_data["recovery_token"]["expiration"]
    )
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] is None


def test_graphql_generate_recovery_key_with_expiration_in_the_past(
    authorized_client, tokens_file
):
    expiration_date = datetime.datetime.now() - datetime.timedelta(minutes=5)
    expiration_date_str = expiration_date.strftime("%Y-%m-%dT%H:%M:%S.%f")

    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "expirationDate": expiration_date_str,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is False
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 400
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is None
    assert "recovery_token" not in read_json(tokens_file)


def test_graphql_generate_recovery_key_with_invalid_time_format(
    authorized_client, tokens_file
):
    expiration_date = "invalid_time_format"
    expiration_date_str = expiration_date

    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "expirationDate": expiration_date_str,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is None

    assert "recovery_token" not in read_json(tokens_file)


def test_graphql_generate_recovery_key_with_limited_uses(
    authorized_client, tokens_file
):

    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "expirationDate": None,
                    "uses": 2,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is not None

    mnemonic_key = response.json()["data"]["getNewRecoveryApiKey"]["key"]
    key = mnemonic_to_hex(mnemonic_key)

    assert read_json(tokens_file)["recovery_token"]["token"] == key
    assert read_json(tokens_file)["recovery_token"]["uses_left"] == 2

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["creationDate"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["expirationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] == 2

    # Try to use token
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": mnemonic_key,
                    "deviceName": "test_token1",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["creationDate"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["expirationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] == 1

    # Try to use token
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": mnemonic_key,
                    "deviceName": "test_token2",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is True
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 200
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is not None

    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={"query": generate_api_query([API_RECOVERY_QUERY])},
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["api"]["recoveryKey"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["exists"] is True
    assert response.json()["data"]["api"]["recoveryKey"]["valid"] is False
    assert response.json()["data"]["api"]["recoveryKey"]["creationDate"] is not None
    assert response.json()["data"]["api"]["recoveryKey"]["expirationDate"] is None
    assert response.json()["data"]["api"]["recoveryKey"]["usesLeft"] == 0

    # Try to use token
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_USE_MUTATION,
            "variables": {
                "input": {
                    "key": mnemonic_key,
                    "deviceName": "test_token3",
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["useRecoveryApiKey"]["success"] is False
    assert response.json()["data"]["useRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["useRecoveryApiKey"]["code"] == 404
    assert response.json()["data"]["useRecoveryApiKey"]["token"] is None


def test_graphql_generate_recovery_key_with_negative_uses(
    authorized_client, tokens_file
):
    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "uses": -1,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is False
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 400
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is None


def test_graphql_generate_recovery_key_with_zero_uses(authorized_client, tokens_file):
    # Try to get token status
    response = authorized_client.post(
        "/graphql",
        json={
            "query": API_RECOVERY_KEY_GENERATE_MUTATION,
            "variables": {
                "limits": {
                    "uses": 0,
                },
            },
        },
    )
    assert response.status_code == 200
    assert response.json().get("data") is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["success"] is False
    assert response.json()["data"]["getNewRecoveryApiKey"]["message"] is not None
    assert response.json()["data"]["getNewRecoveryApiKey"]["code"] == 400
    assert response.json()["data"]["getNewRecoveryApiKey"]["key"] is None