"""Interactive authentication callback for client credentials."""
from typing import Optional
import logging
import re
import urllib.parse

from getpass import getpass
import httpx

from .auth import (
    DEFAULT_GATEWAY_URL,
    WaylayCredentials,
    ClientCredentials,
    DEFAULT_ACCOUNTS_URL,
    ACCOUNTS_USERS_ME_PATH,
    AuthError
)

_http = httpx

log = logging.getLogger(__name__)


def tell(message: str):
    """Show an interactive authentication message."""
    print(message)


def ask(prompt: str) -> str:
    """Prompt user for information."""
    return input(prompt)


def ask_secret(prompt: str) -> str:
    """Prompt user for credential information."""
    return getpass(prompt=prompt)


def ask_yes_no(prompt: str, default: Optional[bool] = None) -> bool:
    """Keep prompting the user until response starts with a true or false character."""
    while True:
        resp = ask(prompt)
        if not resp and default is not None:
            return default
        resp_char = resp.lower()[0]
        if resp_char in ('n', 'f'):
            return False
        if resp_char in ('t', 'y'):
            return True


def ask_gateway(default_gateway_url: str):
    """Ask for the gateway api in an interactive dialog."""
    gateway_url: str = _gateway_url_for(default_gateway_url)
    gateway_validated = False
    while not gateway_validated:
        tell(f'Proposed api gateway: {gateway_url}')
        tell('Please confirm, or specify a gateway by platform id, hostname or url.')
        tell("""Examples:
    'enterprise' (or 'api.waylay.io') for the Enterprise platform,
    'https://waylay-api.mycompany.com' as a custom endpoint url
        """)
        gateway_url = ask(
            f'> Press enter to confirm, or specify an alternate gateway [{gateway_url}]: '
        ) or gateway_url
        gateway_url = _gateway_url_for(gateway_url)
        try:
            gateway_status_resp = httpx.get(f'{gateway_url}{ACCOUNTS_USERS_ME_PATH}')
        except Exception as err:
            tell(f"Cannot connect to '{gateway_url}: {err}")
            gateway_url = _gateway_url_for(default_gateway_url)
        else:
            gateway_status = gateway_status_resp.status_code
            gateway_validated = gateway_status == 401
            if not gateway_validated:
                msg = ('Should require authentication' if gateway_status == 200 else gateway_status_resp.reason_phrase)
                tell(f"Not a gateway URL: '{gateway_url}': {msg}")
                gateway_url = _gateway_url_for(default_gateway_url)
            else:
                tell(f'Using gateway: {gateway_url}')
    return gateway_url


def request_migrate_to_gateway_interactive(profile, msg):
    """Asks to migrate to an api-gateway configuration."""
    tell(msg)
    tell(
        'NOTE: Migrating to an api-gateway will make this '
        'profile unusable for older waylay-py versions.'
    )
    ans = ask_yes_no(f"Migrate '{profile}' to use an api-gateway? [Y/n]", True)
    if not ans:
        tell(f"Not migrating configuration profile '{profile}'")
    return ans


def request_client_credentials_interactive(
    default_gateway_url: str = DEFAULT_GATEWAY_URL
) -> WaylayCredentials:
    """Asks interactively for client credentials.

    Default callback provider for an interactive WaylayConfig.
    """
    tell("Authenticating to the Waylay Platform")
    gateway_url = ask_gateway(default_gateway_url)
    tell("Please provide client credentials for the waylay data client.")
    credentials = ClientCredentials(api_key='', api_secret='', gateway_url=gateway_url)
    retry = 0
    while not credentials.is_well_formed() and retry < 3:
        api_key = ask(prompt='> apiKey : ').strip()
        api_secret = ask_secret(prompt='> apiSecret : ').strip()
        credentials = ClientCredentials(
            api_key=api_key, api_secret=api_secret, gateway_url=gateway_url
        )
        if not credentials.is_well_formed():
            retry += 1
            if retry >= 3:
                tell('Too many attempts, failing authentication')
                raise AuthError('Too many attempts, failing authentication')
            tell('Invalid apiKey or apiSecret, please retry')
    return credentials


def request_client_credentials_interactive_legacy(
    default_accounts_url: str = DEFAULT_ACCOUNTS_URL
) -> WaylayCredentials:
    """Asks interactively for client credentials.

    Default callback provider for an interactive WaylayConfig.
    """
    tell("Authenticating to the Waylay Platform")

    accounts_url: str = default_accounts_url
    acc_validated = False
    while not acc_validated:
        tell(f'Using authentication provider at [{accounts_url}]')
        accounts_url = ask(
            '> alternative endpoint (press enter to continue)?: '
        ) or accounts_url
        accounts_url = _root_url_for(accounts_url)
        accounts_status_resp = httpx.get(accounts_url)
        acc_validated = not accounts_status_resp.is_error
        if acc_validated:
            tell(f"Authenticating at '{accounts_status_resp.json()['name']}'")
        else:
            tell(f"Cannot connect to '{accounts_url}': {accounts_status_resp.reason_phrase}")

    tell("Please provide client credentials for the waylay data client.")
    credentials = ClientCredentials(api_key='', api_secret='', accounts_url=accounts_url)
    retry = 0
    while not credentials.is_well_formed() and retry < 3:
        api_key = ask(prompt='> apiKey : ').strip()
        api_secret = ask_secret(prompt='> apiSecret : ').strip()
        credentials = ClientCredentials(
            api_key=api_key, api_secret=api_secret, accounts_url=accounts_url
        )
        if not credentials.is_well_formed():
            retry += 1
            if retry >= 3:
                tell('Too many attempts, failing authentication')
                raise AuthError('Too many attempts, failing authentication')
            tell('Invalid apiKey or apiSecret, please retry')
    return credentials


def request_store_config_interactive(profile, save_callback):
    """Save interactively the storage of credentials as a profile."""
    if ask_yes_no(
        f'> Do you want to store these credentials with profile={profile}? [Y]: ', True
    ):
        save_location = save_callback()
        tell(
            f"Credential configuration stored as \n\t{save_location}\n"
            "Please make sure this file is treated securely.\n"
            "If compromised, _Revoke_ the api-key on the Waylay Console!"
        )


def _gateway_url_for(url_input: str):
    """Infer the gateway url from user input or existing accounts url."""
    url_input = (url_input or '').lower()
    if 'accounts-api' in url_input:
        url_input = url_input.replace('accounts-api', 'api')
    elif url_input in ('api', '', 'enterprise'):
        url_input = 'api.waylay.io'
    elif re.match(r'^[a-z0-9]+$', url_input):
        url_input = f'api-{url_input}.waylay.io'
    return _root_url_for(url_input)


def _root_url_for(host_or_url: str) -> str:
    scheme, loc, path, query, fragment = urllib.parse.urlsplit(host_or_url)

    if not scheme and not loc:
        # make sure any host name is converted in a https:// url
        scheme = 'https'
        loc = path
        path = ''

    if path.endswith('/'):
        # tenant settings root urls are without trailing slash
        log.warning('Trailing slashes trimmed: %s', host_or_url)
        path = path.rstrip('/')

    return urllib.parse.urlunsplit([scheme, loc, path, query, fragment])
