from cryptography.fernet import InvalidToken
from cryptography.exceptions import InvalidKey, InvalidSignature
import logging
from typing import Optional, TYPE_CHECKING

from django.contrib.auth import get_user_model
from django.conf import settings
from encipher.models import EncryptionKey

if TYPE_CHECKING:
    from sandbox.latchstring.models import User
else:
    User = get_user_model()

logger = logging.getLogger(__name__)


def get_or_create_public_key(user: 'User', name: str) -> Optional[EncryptionKey]:
    """
    Helper method for creating / retrieving Encryption Key pairs for API auth.
    """
    # private keys can't be loaded from database if this key isn't set
    if not settings.PRIVATE_KEY_PASSWORD_PREFIX:
        logger.error(
            'Failed to get %s Public Key - Private key password not set; setup incomplete!', user
        )
        return

    try:
        return (
            EncryptionKey.objects.active()
            .filter(
                name=name,
                user=user,
            )
            .select_related('user')
            .latest('created')
        )
    except (InvalidKey, InvalidSignature, InvalidToken) as e:
        logger.exception('Encryption keys are invalid: %s' % e)
        return
    except EncryptionKey.DoesNotExist:
        pass

    if not user or not user.is_api:
        msg = 'User is not defined!' if user else 'User is not an API user!'
        logger.error('Unable to create Key Pair - %s', msg)
        return

    try:
        return EncryptionKey.objects.create_ssh_keys(name=name, user=user)
    except Exception as e:
        logger.exception('Unable to create Key Pair for %s - %s', user, e)
