"""API access mutations"""
# pylint: disable=too-few-public-methods
import datetime
import typing
import strawberry
from strawberry.types import Info
from selfprivacy_api.actions.api_tokens import (
    CannotDeleteCallerException,
    InvalidExpirationDate,
    InvalidUsesLeft,
    NotFoundException,
    delete_api_token,
    get_new_api_recovery_key,
    use_mnemonic_recovery_token,
    refresh_api_token,
    delete_new_device_auth_token,
    get_new_device_auth_token,
    use_new_device_auth_token,
)
from selfprivacy_api.graphql import IsAuthenticated
from selfprivacy_api.graphql.mutations.mutation_interface import (
    GenericMutationReturn,
    MutationReturnInterface,
)


@strawberry.type
class ApiKeyMutationReturn(MutationReturnInterface):
    key: typing.Optional[str]


@strawberry.type
class DeviceApiTokenMutationReturn(MutationReturnInterface):
    token: typing.Optional[str]


@strawberry.input
class RecoveryKeyLimitsInput:
    """Recovery key limits input"""

    expiration_date: typing.Optional[datetime.datetime] = None
    uses: typing.Optional[int] = None


@strawberry.input
class UseRecoveryKeyInput:
    """Use recovery key input"""

    key: str
    deviceName: str


@strawberry.input
class UseNewDeviceKeyInput:
    """Use new device key input"""

    key: str
    deviceName: str


@strawberry.type
class ApiMutations:
    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def get_new_recovery_api_key(
        self, limits: typing.Optional[RecoveryKeyLimitsInput] = None
    ) -> ApiKeyMutationReturn:
        """Generate recovery key"""
        if limits is None:
            limits = RecoveryKeyLimitsInput()
        try:
            key = get_new_api_recovery_key(limits.expiration_date, limits.uses)
        except InvalidExpirationDate:
            return ApiKeyMutationReturn(
                success=False,
                message="Expiration date must be in the future",
                code=400,
                key=None,
            )
        except InvalidUsesLeft:
            return ApiKeyMutationReturn(
                success=False,
                message="Uses must be greater than 0",
                code=400,
                key=None,
            )
        return ApiKeyMutationReturn(
            success=True,
            message="Recovery key generated",
            code=200,
            key=key,
        )

    @strawberry.mutation()
    def use_recovery_api_key(
        self, input: UseRecoveryKeyInput
    ) -> DeviceApiTokenMutationReturn:
        """Use recovery key"""
        token = use_mnemonic_recovery_token(input.key, input.deviceName)
        if token is not None:
            return DeviceApiTokenMutationReturn(
                success=True,
                message="Recovery key used",
                code=200,
                token=token,
            )
        else:
            return DeviceApiTokenMutationReturn(
                success=False,
                message="Recovery key not found",
                code=404,
                token=None,
            )

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def refresh_device_api_token(self, info: Info) -> DeviceApiTokenMutationReturn:
        """Refresh device api token"""
        token_string = (
            info.context["request"]
            .headers.get("Authorization", "")
            .replace("Bearer ", "")
        )
        if token_string is None:
            return DeviceApiTokenMutationReturn(
                success=False,
                message="Token not found",
                code=404,
                token=None,
            )

        try:
            new_token = refresh_api_token(token_string)
            return DeviceApiTokenMutationReturn(
                success=True,
                message="Token refreshed",
                code=200,
                token=new_token,
            )
        except NotFoundException:
            return DeviceApiTokenMutationReturn(
                success=False,
                message="Token not found",
                code=404,
                token=None,
            )

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def delete_device_api_token(self, device: str, info: Info) -> GenericMutationReturn:
        """Delete device api token"""
        self_token = (
            info.context["request"]
            .headers.get("Authorization", "")
            .replace("Bearer ", "")
        )
        try:
            delete_api_token(self_token, device)
        except NotFoundException:
            return GenericMutationReturn(
                success=False,
                message="Token not found",
                code=404,
            )
        except CannotDeleteCallerException:
            return GenericMutationReturn(
                success=False,
                message="Cannot delete caller token",
                code=400,
            )
        except Exception as e:
            return GenericMutationReturn(
                success=False,
                message=str(e),
                code=500,
            )
        return GenericMutationReturn(
            success=True,
            message="Token deleted",
            code=200,
        )

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def get_new_device_api_key(self) -> ApiKeyMutationReturn:
        """Generate device api key"""
        key = get_new_device_auth_token()
        return ApiKeyMutationReturn(
            success=True,
            message="Device api key generated",
            code=200,
            key=key,
        )

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def invalidate_new_device_api_key(self) -> GenericMutationReturn:
        """Invalidate new device api key"""
        delete_new_device_auth_token()
        return GenericMutationReturn(
            success=True,
            message="New device key deleted",
            code=200,
        )

    @strawberry.mutation()
    def authorize_with_new_device_api_key(
        self, input: UseNewDeviceKeyInput
    ) -> DeviceApiTokenMutationReturn:
        """Authorize with new device api key"""
        token = use_new_device_auth_token(input.key, input.deviceName)
        if token is None:
            return DeviceApiTokenMutationReturn(
                success=False,
                message="Token not found",
                code=404,
                token=None,
            )
        return DeviceApiTokenMutationReturn(
            success=True,
            message="Token used",
            code=200,
            token=token,
        )