from toot import api, config, User, App
from toot.entities import from_dict, Instance
from toot.exceptions import ApiError, ConsoleError
from urllib.parse import urlparse


def find_instance(base_url: str) -> Instance:
    try:
        instance = api.get_instance(base_url).json()
        return from_dict(Instance, instance)
    except Exception:
        raise ConsoleError(f"Instance not found at {base_url}")


def register_app(domain: str, base_url: str) -> App:
    try:
        response = api.create_app(base_url)
    except ApiError:
        raise ConsoleError("Registration failed.")

    app = App(domain, base_url, response['client_id'], response['client_secret'])
    config.save_app(app)

    return app


def get_or_create_app(base_url: str) -> App:
    instance = find_instance(base_url)
    domain = _get_instance_domain(instance)
    return config.load_app(domain) or register_app(domain, base_url)


def create_user(app: App, access_token: str) -> User:
    # Username is not yet known at this point, so fetch it from Mastodon
    user = User(app.instance, None, access_token)
    creds = api.verify_credentials(app, user).json()

    user = User(app.instance, creds["username"], access_token)
    config.save_user(user, activate=True)

    return user


def login_username_password(app: App, email: str, password: str) -> User:
    try:
        response = api.login(app, email, password)
    except Exception:
        raise ConsoleError("Login failed")

    return create_user(app, response["access_token"])


def login_auth_code(app: App, authorization_code: str) -> User:
    try:
        response = api.request_access_token(app, authorization_code)
    except Exception:
        raise ConsoleError("Login failed")

    return create_user(app, response["access_token"])


def _get_instance_domain(instance: Instance) -> str:
    """Extracts the instance domain name.

    Pleroma and its forks return an actual URI here, rather than a domain name
    like Mastodon. This is contrary to the spec.¯ in that case, parse out the
    domain and return it.

    TODO: when updating to v2 instance endpoint, this field has been renamed to
    `domain`
    """
    if instance.uri.startswith("http"):
        return urlparse(instance.uri).netloc
    return instance.uri