"""
auth.py
author: @mbiokyle29 / @boydgreenfield
- Adapted from v0 auth.py
"""
from __future__ import print_function
import click
import datetime
import errno
from functools import wraps
import json
import logging
import os
import sys

from onecodex.api import Api
from onecodex.lib.auth import check_version, fetch_api_key_from_uname
from onecodex.utils import collapse_user
from onecodex.version import __version__


log = logging.getLogger(__name__)
DATE_FORMAT = "%Y-%m-%d %H:%M"
API_KEY_LEN = 32


def login_uname_pwd(server, api_key=None):
    """
    Prompts user for username and password, gets API key from server
    if not provided.
    """
    username = click.prompt('Please enter your One Codex (email)')
    if api_key is not None:
        return username, api_key

    password = click.prompt('Please enter your password (typing will be hidden)',
                            hide_input=True)

    # fetch_api_key expects server to end in /
    if server[-1] != '/':
        server = server + '/'

    # now get the API key
    api_key = fetch_api_key_from_uname(username, password, server)
    return username, api_key


def _login(server=None, creds_file=None, api_key=None, silent=False):
    """
    Login main function
    """
    # creds file path setup
    if creds_file is None:
        creds_file = os.path.expanduser('~/.onecodex')

    # check if the creds file exists and is readable
    if not os.path.exists(creds_file):
        if silent:
            return None

        creds = {}
    elif not os.access(creds_file, os.R_OK):
        click.echo('Please check the permissions on {}'.format(collapse_user(creds_file)),
                   err=True)
        sys.exit(1)
    else:
        # it is, so let's read it!
        with open(creds_file, 'r') as fp:
            try:
                creds = json.load(fp)

                # this is just so the exception is triggered if api_key and e-mail aren't there
                creds_email = creds['email']
                creds_api_key = creds['api_key']
            except (KeyError, ValueError):
                click.echo('Your ~/.onecodex credentials file appears to be corrupted. '  # noqa
                           'Please delete it and re-authorize.', err=True)
                sys.exit(1)

        # check for updates if logged in more than one day ago
        last_update = creds['updated_at'] if creds.get('updated_at') else creds['saved_at']
        diff = datetime.datetime.now() - datetime.datetime.strptime(last_update,
                                                                    DATE_FORMAT)
        if diff.days >= 1:
            # if creds_file is old, check for updates
            upgrade_required, msg = check_version(__version__, 'https://app.onecodex.com/')
            creds['updated_at'] = datetime.datetime.now().strftime(DATE_FORMAT)

            try:
                json.dump(creds, open(creds_file, 'w'))
            except Exception as e:
                if e.errno == errno.EACCES:
                    click.echo('Please check the permissions on {}'
                               .format(collapse_user(creds_file)),
                               err=True)
                    sys.exit(1)
                else:
                    raise

            if upgrade_required:
                click.echo('\nWARNING: {}\n'.format(msg), err=True)

        # finally, give the user back what they want (whether silent or not)
        if silent:
            return creds_api_key

        click.echo('Credentials file already exists ({})'.format(collapse_user(creds_file)),
                   err=True)
        return creds_email

    # creds_file was not found and we're not silent, so prompt user to login
    email, api_key = login_uname_pwd(server, api_key=api_key)

    if api_key is None:
        click.echo('We could not verify your credentials. Either you mistyped your email '
                   'or password, or your account does not currently have API access. '
                   'Please contact help@onecodex.com if you continue to experience problems.')
        sys.exit(1)

    creds.update({
        'api_key': api_key,
        'saved_at': datetime.datetime.now().strftime(DATE_FORMAT),
        'updated_at': None,
        'email': email,
    })

    try:
        json.dump(creds, open(creds_file, 'w'))
    except Exception as e:
        if e.errno == errno.EACCES:
            click.echo('Please check the permissions on {}'.format(creds_file),
                       err=True)
            sys.exit(1)
        else:
            raise

    click.echo('Your ~/.onecodex credentials file was successfully created.', err=True)

    return email


def _remove_creds(creds_file=None):
    """
    Remove ~/.onecodex file, returning True if successul or False if the file didn't exist
    """
    if creds_file is None:
        creds_file = os.path.expanduser('~/.onecodex')

    try:
        os.remove(creds_file)
    except Exception as e:
        if e.errno == errno.ENOENT:
            return False
        elif e.errno == errno.EACCES:
            click.echo('Please check the permissions on {}'.format(collapse_user(creds_file)),
                       err=True)
            sys.exit(1)
        else:
            raise

    return True


def _logout(creds_file=None):
    """
    Logout main function, just rm ~/.onecodex more or less
    """
    if _remove_creds(creds_file=creds_file):
        click.echo('Successfully removed One Codex credentials.', err=True)
        sys.exit(0)
    else:
        click.echo('No One Codex API keys found.', err=True)
        sys.exit(1)


def login_required(fn):
    """
    Decorator for the CLI for requiring login before proceeding.
    """

    @wraps(fn)
    def login_wrapper(ctx, *args, **kwargs):
        if 'API_KEY' in ctx.obj and ctx.obj['API_KEY'] is not None:
            ctx.obj['API'] = Api(api_key=ctx.obj['API_KEY'], telemetry=ctx.obj['TELEMETRY'])
        else:
            # try and find it
            api_key = _login(silent=True)
            if api_key is not None:
                ctx.obj['API'] = Api(api_key=api_key, telemetry=ctx.obj['TELEMETRY'])
            else:
                click.echo('The command you specified requires authentication. Please login first.\n', err=True)
                ctx.exit()

        return fn(ctx, *args, **kwargs)

    return login_wrapper

@@ -1834,17 +1834,16 @@
 oad(fp)%0A
-%0A
         
@@ -1850,215 +1850,14 @@
     
-    # this is just so the exception is triggered if api_key and e-mail aren't there%0A                creds_email = creds%5B'email'%5D%0A                creds_api_key = creds%5B'api_key'%5D%0A            except (KeyError,
+except
  Val
@@ -1863,17 +1863,16 @@
 lueError
-)
 :%0A      
@@ -2152,25 +2152,29 @@
 date = creds
-%5B
+.get(
 'updated_at'
@@ -2177,12 +2177,9 @@
 _at'
-%5D if
+,
  cre
@@ -2190,43 +2190,119 @@
 et('
-updated_at') else creds%5B'saved_at'%5D
+saved_at'))%0A        last_update = last_update if last_update else datetime.datetime.now().strftime(DATE_FORMAT)
 %0A   
@@ -3345,24 +3345,37 @@
 rn creds
-_
+.get('
 api_key
+', None)
 %0A%0A      
@@ -3424,16 +3424,31 @@
 sts (%7B%7D)
+. Logout first.
 '.format
@@ -3529,14 +3529,27 @@
 reds
-_email
+.get('email', None)
 %0A%0A  

Fix bug when logged in anonymously via API CLI expects authorization, not anonymous login. Creds containing anonymous login broke auth.