from time import time
from typing import Dict

from werkzeug.security import gen_salt

from fittrackee.users.models import User

from .exceptions import InvalidOAuth2Scopes
from .models import OAuth2Client

VALID_SCOPES = [
    'application:write',
    'profile:read',
    'profile:write',
    'users:read',
    'users:write',
    'workouts:read',
    'workouts:write',
]


def check_scope(scope: str) -> str:
    """
    Verify if provided scope is valid.
    """
    if not isinstance(scope, str) or not scope:
        raise InvalidOAuth2Scopes()

    valid_scopes = []
    scopes = scope.split()
    for value in scopes:
        if value in VALID_SCOPES:
            valid_scopes.append(value)

    if not valid_scopes:
        raise InvalidOAuth2Scopes()

    return ' '.join(valid_scopes)


def create_oauth2_client(metadata: Dict, user: User) -> OAuth2Client:
    """
    Create OAuth2 client for 3rd-party applications.

    Only Authorization Code Grant with 'client_secret_post' as method
    is supported.
    Code challenge can be used if provided on authorization.
    """
    client_metadata = {
        'client_name': metadata['client_name'],
        'client_description': metadata.get('client_description'),
        'client_uri': metadata['client_uri'],
        'redirect_uris': metadata['redirect_uris'],
        'scope': check_scope(metadata['scope']),
        'grant_types': ['authorization_code', 'refresh_token'],
        'response_types': ['code'],
        'token_endpoint_auth_method': 'client_secret_post',
    }
    client_id = gen_salt(24)
    client_id_issued_at = int(time())
    client = OAuth2Client(
        client_id=client_id,
        client_id_issued_at=client_id_issued_at,
        user_id=user.id,
    )
    client.set_client_metadata(client_metadata)
    client.client_secret = gen_salt(48)
    return client