"""
Action-level tests of ssh
(For API-independent logic incl. connection to persistent storage)
"""

import pytest
from typing import Optional

from selfprivacy_api.actions.ssh import (
    set_ssh_settings,
    get_ssh_settings,
    create_ssh_key,
    remove_ssh_key,
    KeyNotFound,
    UserNotFound,
)
from selfprivacy_api.repositories.users.json_user_repository import (
    get_users,
    get_user_by_username,
)
from selfprivacy_api.utils import WriteUserData, ReadUserData
from selfprivacy_api.models.user import UserDataUserOrigin


@pytest.fixture(params=[True, False])
def bool_value(request):
    return request.param


@pytest.fixture(
    params=[
        "normal_populated_json",
        "deleted_enabled",
        "deleted_auth",
        "empty",
        "ssh_not_in_json",
    ]
)
def possibly_undefined_ssh_settings(generic_userdata, request, bool_value):
    with WriteUserData() as data:
        data["ssh"] = {"enable": bool_value, "passswordAuthentication": bool_value}
    assert get_raw_json_ssh_setting("enable") == bool_value
    assert get_raw_json_ssh_setting("passswordAuthentication") == bool_value

    if request.param == "deleted_enabled":
        with WriteUserData() as data:
            del data["ssh"]["enable"]

    if request.param == "deleted_auth":
        with WriteUserData() as data:
            del data["ssh"]["passswordAuthentication"]

    if request.param == "empty":
        with WriteUserData() as data:
            del data["ssh"]["passswordAuthentication"]
            del data["ssh"]["enable"]

    if request.param == "ssh_not_in_json":
        with WriteUserData() as data:
            del data["ssh"]


@pytest.fixture(params=[True, False, None])
def ssh_enable_spectrum(request):
    return request.param


@pytest.fixture(params=[True, False, None])
def password_auth_spectrum(request):
    return request.param


def admin_name() -> Optional[str]:
    users = get_users()
    for user in users:
        if user.user_type == UserDataUserOrigin.PRIMARY:
            return user.username
    return None


def get_raw_json_ssh_setting(setting: str):
    with ReadUserData() as data:
        return (data.get("ssh") or {}).get(setting)


def test_read_json(possibly_undefined_ssh_settings):
    with ReadUserData() as data:
        if "ssh" not in data.keys():
            assert get_ssh_settings().enable is not None
            assert get_ssh_settings().passwordAuthentication is not None

            # TODO: Is it really a good idea to have password ssh enabled by default?
            assert get_ssh_settings().enable is True
            assert get_ssh_settings().passwordAuthentication is True
            return

        if "enable" not in data["ssh"].keys():
            assert get_ssh_settings().enable is True
        else:
            assert get_ssh_settings().enable == data["ssh"]["enable"]

        if "passwordAuthentication" not in data["ssh"].keys():
            assert get_ssh_settings().passwordAuthentication is False
        else:
            assert (
                get_ssh_settings().passwordAuthentication
                == data["ssh"]["passwordAuthentication"]
            )


def test_enabling_disabling_writes_json(
    possibly_undefined_ssh_settings, ssh_enable_spectrum, password_auth_spectrum
):
    original_enable = get_raw_json_ssh_setting("enable")
    original_password_auth = get_raw_json_ssh_setting("passwordAuthentication")

    set_ssh_settings(ssh_enable_spectrum, password_auth_spectrum)

    with ReadUserData() as data:
        if ssh_enable_spectrum is None:
            assert get_raw_json_ssh_setting("enable") == original_enable
        else:
            assert get_raw_json_ssh_setting("enable") == ssh_enable_spectrum

        if password_auth_spectrum is None:
            assert (
                get_raw_json_ssh_setting("passwordAuthentication")
                == original_password_auth
            )
        else:
            assert (
                get_raw_json_ssh_setting("passwordAuthentication")
                == password_auth_spectrum
            )


############### ROOTKEYS


def test_read_root_keys_from_json(generic_userdata):
    assert get_ssh_settings().rootKeys == ["ssh-ed25519 KEY test@pc"]
    new_keys = ["ssh-ed25519 KEY test@pc", "ssh-ed25519 KEY2 test@pc"]

    with WriteUserData() as data:
        data["ssh"]["rootKeys"] = new_keys

    assert get_ssh_settings().rootKeys == new_keys

    with WriteUserData() as data:
        del data["ssh"]["rootKeys"]

    assert get_ssh_settings().rootKeys == []

    with WriteUserData() as data:
        del data["ssh"]

    assert get_ssh_settings().rootKeys == []


def test_removing_root_key_writes_json(generic_userdata):
    # generic userdata has a a single root key
    rootkeys = get_ssh_settings().rootKeys
    assert len(rootkeys) == 1
    key1 = rootkeys[0]
    key2 = "ssh-rsa MYSUPERKEY root@pc"

    create_ssh_key("root", key2)
    rootkeys = get_ssh_settings().rootKeys
    assert len(rootkeys) == 2

    remove_ssh_key("root", key2)
    with ReadUserData() as data:
        assert "ssh" in data
        assert "rootKeys" in data["ssh"]
        assert data["ssh"]["rootKeys"] == [key1]

    remove_ssh_key("root", key1)
    with ReadUserData() as data:
        assert "ssh" in data
        assert "rootKeys" in data["ssh"]
        assert data["ssh"]["rootKeys"] == []


def test_remove_root_key_on_undefined(generic_userdata):
    # generic userdata has a a single root key
    rootkeys = get_ssh_settings().rootKeys
    assert len(rootkeys) == 1
    key1 = rootkeys[0]

    with WriteUserData() as data:
        del data["ssh"]["rootKeys"]

    with pytest.raises(KeyNotFound):
        remove_ssh_key("root", key1)
    rootkeys = get_ssh_settings().rootKeys
    assert len(rootkeys) == 0

    with WriteUserData() as data:
        del data["ssh"]

    with pytest.raises(KeyNotFound):
        remove_ssh_key("root", key1)
    rootkeys = get_ssh_settings().rootKeys
    assert len(rootkeys) == 0


def test_adding_root_key_writes_json(generic_userdata):
    with WriteUserData() as data:
        del data["ssh"]
    key1 = "ssh-ed25519 KEY test@pc"
    key2 = "ssh-ed25519 KEY2 test@pc"
    create_ssh_key("root", key1)

    with ReadUserData() as data:
        assert "ssh" in data
        assert "rootKeys" in data["ssh"]
        assert data["ssh"]["rootKeys"] == [key1]

    with WriteUserData() as data:
        del data["ssh"]["rootKeys"]
    create_ssh_key("root", key1)

    with ReadUserData() as data:
        assert "ssh" in data
        assert "rootKeys" in data["ssh"]
        assert data["ssh"]["rootKeys"] == [key1]

    create_ssh_key("root", key2)

    with ReadUserData() as data:
        assert "ssh" in data
        assert "rootKeys" in data["ssh"]
        # order is irrelevant
        assert set(data["ssh"]["rootKeys"]) == set([key1, key2])


############### ADMIN KEYS


def test_read_admin_keys_from_json(generic_userdata):
    admin_name = "tester"
    assert get_user_by_username(admin_name).ssh_keys == ["ssh-rsa KEY test@pc"]
    new_keys = ["ssh-rsa KEY test@pc", "ssh-ed25519 KEY2 test@pc"]

    with WriteUserData() as data:
        data["sshKeys"] = new_keys

    assert get_user_by_username(admin_name).ssh_keys == new_keys

    with WriteUserData() as data:
        del data["sshKeys"]

    assert get_user_by_username(admin_name).ssh_keys == []


def test_adding_admin_key_writes_json(generic_userdata):
    admin_name = "tester"

    with WriteUserData() as data:
        del data["sshKeys"]
    key1 = "ssh-ed25519 KEY test@pc"
    key2 = "ssh-ed25519 KEY2 test@pc"
    create_ssh_key(admin_name, key1)

    with ReadUserData() as data:
        assert "sshKeys" in data
        assert data["sshKeys"] == [key1]

    create_ssh_key(admin_name, key2)

    with ReadUserData() as data:
        assert "sshKeys" in data
        # order is irrelevant
        assert set(data["sshKeys"]) == set([key1, key2])


def test_removing_admin_key_writes_json(generic_userdata):
    # generic userdata has a a single admin key
    admin_name = "tester"

    admin_keys = get_user_by_username(admin_name).ssh_keys
    assert len(admin_keys) == 1
    key1 = admin_keys[0]
    key2 = "ssh-rsa MYSUPERKEY admin@pc"

    create_ssh_key(admin_name, key2)
    admin_keys = get_user_by_username(admin_name).ssh_keys
    assert len(admin_keys) == 2

    remove_ssh_key(admin_name, key2)

    with ReadUserData() as data:
        assert "sshKeys" in data
        assert data["sshKeys"] == [key1]

    remove_ssh_key(admin_name, key1)
    with ReadUserData() as data:
        assert "sshKeys" in data
        assert data["sshKeys"] == []


def test_remove_admin_key_on_undefined(generic_userdata):
    # generic userdata has a a single admin key
    admin_name = "tester"

    admin_keys = get_user_by_username(admin_name).ssh_keys
    assert len(admin_keys) == 1
    key1 = admin_keys[0]

    with WriteUserData() as data:
        del data["sshKeys"]

    with pytest.raises(KeyNotFound):
        remove_ssh_key(admin_name, key1)
    admin_keys = get_user_by_username(admin_name).ssh_keys
    assert len(admin_keys) == 0


############### USER KEYS

regular_users = ["user1", "user2", "user3"]


def find_user_index_in_json_users(users: list, username: str) -> Optional[int]:
    for i, user in enumerate(users):
        if user["username"] == username:
            return i
    return None


@pytest.mark.parametrize("username", regular_users)
def test_read_user_keys_from_json(generic_userdata, username):
    old_keys = [f"ssh-rsa KEY {username}@pc"]
    assert get_user_by_username(username).ssh_keys == old_keys
    new_keys = ["ssh-rsa KEY test@pc", "ssh-ed25519 KEY2 test@pc"]

    with WriteUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        data["users"][user_index]["sshKeys"] = new_keys

    assert get_user_by_username(username).ssh_keys == new_keys

    with WriteUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        del data["users"][user_index]["sshKeys"]

    assert get_user_by_username(username).ssh_keys == []

    # deeper deletions are for user getter tests, not here


@pytest.mark.parametrize("username", regular_users)
def test_adding_user_key_writes_json(generic_userdata, username):
    with WriteUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        del data["users"][user_index]["sshKeys"]
    key1 = "ssh-ed25519 KEY test@pc"
    key2 = "ssh-ed25519 KEY2 test@pc"
    create_ssh_key(username, key1)

    with ReadUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        assert "sshKeys" in data["users"][user_index]
        assert data["users"][user_index]["sshKeys"] == [key1]

    create_ssh_key(username, key2)

    with ReadUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        assert "sshKeys" in data["users"][user_index]
        # order is irrelevant
        assert set(data["users"][user_index]["sshKeys"]) == set([key1, key2])


@pytest.mark.parametrize("username", regular_users)
def test_removing_user_key_writes_json(generic_userdata, username):
    # generic userdata has a a single user key

    user_keys = get_user_by_username(username).ssh_keys
    assert len(user_keys) == 1
    key1 = user_keys[0]
    key2 = "ssh-rsa MYSUPERKEY admin@pc"

    create_ssh_key(username, key2)
    user_keys = get_user_by_username(username).ssh_keys
    assert len(user_keys) == 2

    remove_ssh_key(username, key2)

    with ReadUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        assert "sshKeys" in data["users"][user_index]
        assert data["users"][user_index]["sshKeys"] == [key1]

    remove_ssh_key(username, key1)
    with ReadUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        assert "sshKeys" in data["users"][user_index]
        assert data["users"][user_index]["sshKeys"] == []


@pytest.mark.parametrize("username", regular_users)
def test_remove_user_key_on_undefined(generic_userdata, username):
    # generic userdata has a a single user key
    user_keys = get_user_by_username(username).ssh_keys
    assert len(user_keys) == 1
    key1 = user_keys[0]

    with WriteUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        del data["users"][user_index]["sshKeys"]

    with pytest.raises(KeyNotFound):
        remove_ssh_key(username, key1)

    user_keys = get_user_by_username(username).ssh_keys
    assert len(user_keys) == 0

    with WriteUserData() as data:
        user_index = find_user_index_in_json_users(data["users"], username)
        del data["users"][user_index]

    with pytest.raises(UserNotFound):
        remove_ssh_key(username, key1)

    with WriteUserData() as data:
        del data["users"]

    with pytest.raises(UserNotFound):
        remove_ssh_key(username, key1)