import json
import os
from functools import wraps

import click
from flask import Flask, jsonify, render_template
from flask_simplelogin import SimpleLogin, login_required
from werkzeug.security import check_password_hash, generate_password_hash


# [ -- Utils -- ]

def validate_login(user):
    db_users = json.load(open('users.json'))
    if not db_users.get(user['username']):
        return False
    stored_password = db_users[user['username']]['password']
    if check_password_hash(stored_password, user['password']):
        return True
    return False


def create_user(**data):
    """Creates user with encrypted password"""
    if 'username' not in data or 'password' not in data:
        raise ValueError('username and password are required.')

    # Hash the user password
    data['password'] = generate_password_hash(
        data.pop('password'),
        method='pbkdf2:sha256'
    )

    # Here you insert the `data` in your users database
    # for this simple example we are recording in a json file
    db_users = json.load(open('users.json'))
    # add the new created user to json
    db_users[data['username']] = data
    # commit changes to database
    json.dump(db_users, open('users.json', 'w'))
    return data


# [--- Flask Factories  ---]

def create_app():
    app = Flask(__name__)
    app.config.from_object('settings')
    return app


def configure_extensions(app):
    SimpleLogin(app, login_checker=validate_login)
    if not os.path.exists('users.json'):
        with open('users.json', 'a') as json_file:
            # This just touch create a new dbfile
            json.dump({'username': '', 'password': ''}, json_file)


def configure_views(app):
    @app.route('/')
    def index():
        return render_template('index.html')

    @app.route('/secret')
    @login_required()
    def secret():
        return render_template('secret.html')

    @app.route('/api', methods=['POST'])
    @login_required(basic=True)
    def api():
        return jsonify(data='You are logged in with basic auth')

    @app.route('/complex')
    @login_required(username=['admin'])
    def complexview():
        return render_template('secret.html')


# [--- Command line functions ---]

def with_app(f):
    """Calls function passing app as first argument"""
    @wraps(f)
    def decorator(*args, **kwargs):
        app = create_app()
        configure_extensions(app)
        configure_views(app)
        return f(app=app, *args, **kwargs)
    return decorator


@click.group()
def main():
    """Flask Simple Login Example App"""


@main.command()
@click.option('--username', required=True, prompt=True)
@click.option('--password', required=True, prompt=True, hide_input=True,
              confirmation_prompt=True)
@with_app
def adduser(app, username, password):
    """Add new user with admin access"""
    with app.app_context():
        create_user(username=username, password=password)
        click.echo('user created!')


@main.command()
@click.option('--reloader/--no-reloader', default=None)
@click.option('--debug/--no-debug', default=None)
@click.option('--host', default=None)
@click.option('--port', default=None)
@with_app
def runserver(app=None, reloader=None, debug=None, host=None, port=None):
    """Run the Flask development server i.e. app.run()"""
    debug = debug or app.config.get('DEBUG', False)
    reloader = reloader or app.config.get('RELOADER', False)
    host = host or app.config.get('HOST', '127.0.0.1')
    port = port or app.config.get('PORT', 5000)
    app.run(
        use_reloader=reloader,
        debug=debug,
        host=host,
        port=port
    )


# [--- Entry point ---]

if __name__ == "__main__":
    # python manage.py to see help
    main()