import base64
import os

from flask import (render_template, flash, abort, request, redirect, jsonify,
                   send_from_directory, g, url_for as _url_for)
from flask_googlelogin import UserMixin, login_user, logout_user, current_user
from werkzeug import secure_filename
import misaka as m

from .constants import (DEFAULT_LANG, DEFAULT_PLATFORM, AVAILABLE_PLATFORMS,
                        ALLOWED_EXTS)
from .extensions import login, db
from .forms import CategoryForm, QuestionForm
from .helpers import url_for
from .models import Category, Question


def init(app):

    @app.before_request
    def detect_platform():
        user_agent = request.headers.get('User-Agent').lower()
        if 'windows' in user_agent:
            g.detected_platform = 'win'
        elif 'macintosh' in user_agent:
            g.detected_platform = 'mac'
        elif 'linux' in user_agent:
            g.detected_platform = 'linux'

    @app.before_request
    def db_session_start():
        if request.endpoint and request.endpoint != 'static':
            db.session()


    @app.teardown_request
    def db_session_commit(exception=None):
        if request.endpoint and request.endpoint != 'static':
            if exception:
                db.session.rollback()
            else:
                db.session.commit()
            db.session.remove()


    @app.context_processor
    def common():
        params = request.view_args
        available_platforms = []
        for platform, platform_title in AVAILABLE_PLATFORMS:
            if platform == DEFAULT_PLATFORM:
                continue
            params['platform'] = platform
            available_platforms.append((platform, platform_title,
                                        _url_for(request.endpoint, **params)))
        return dict(
            m=m,
            url_for=url_for,
            current_user=current_user,
            selected_lang=request.args.get('lang', DEFAULT_LANG),
            selected_platform=request.args.get('platform', g.detected_platform),
            available_platforms=available_platforms,
            categories=Category.query.order_by(Category.title).all())


    @login.user_loader
    def get_user(user_id):
      user = UserMixin()
      user.id = user_id
      user.is_authenticated = lambda: user.id in app.config['ALLOWED_USERS']
      return user


    # Routes


    @app.route('/login')
    def login_redirect():
        return redirect(login.login_url())


    @app.route('/oauth2callback')
    @login.oauth2callback
    def oauth2callback(token, userinfo, **params):
        user = login.login_manager.user_callback(userinfo['id'])
        login_user(user)
        return redirect(url_for('index'))


    @app.route('/logout')
    def logout():
        logout_user()
        return redirect(url_for('index'))


    @app.route('/')
    def index():
        selected_platform = request.args.get('platform', g.detected_platform)
        questions = Question.get_sticky(platform=selected_platform)
        return render_template('paano/index.html', questions=questions)


    @app.route('/new_category', methods=['GET', 'POST'])
    def new_category():
        form = CategoryForm(prefix='paano-category')
        if current_user.is_authenticated() and form.validate_on_submit():
            category = Category.create(form.title.data)
            db.session.add(category)
            db.session.flush()
            flash("Category saved")
            return redirect(category.url())
        return render_template('paano/new_category.html', form=form)


    @app.route('/new_question', methods=['GET', 'POST'])
    def new_question():
        form_args = dict(prefix='paano-question')

        category_id = request.args.get('category_id')
        eid = request.args.get('eid')
        if category_id and eid:
            base_question = Question.query.filter_by(category_id=category_id,
                                                     eid=eid).first()
            if base_question.platform == DEFAULT_PLATFORM:
                abort(400)
            if not base_question:
                abort(404)
            form_args['obj'] = base_question

        form = QuestionForm(**form_args)

        form.category_id.choices = [(c.id, c.title) for c in
                                    Category.query.order_by(Category.title).all()]
        if current_user.is_authenticated() and form.validate_on_submit():
            question = Question()
            question.eid = eid
            question.creator = current_user.id
            form.populate_obj(question)
            db.session.add(question)
            db.session.flush()
            flash("Question saved")
            return redirect(question.url(platform=question.platform))
        else:
            if request.method == 'POST':
                print form.errors

        return render_template('paano/new_question.html', form=form)


    @app.route('/categories/<category_id>', methods=['GET', 'POST', 'DELETE'])
    def category(category_id):
        category = Category.query.get_or_404(category_id)
        if current_user.is_authenticated() and request.method == 'DELETE':
            Question.query.filter_by(category_id=category.id).delete()
            db.session.delete(category)
            return jsonify(success=True)
        selected_platform = request.args.get('platform', g.detected_platform)
        questions = category.get_questions(platform=selected_platform)
        form = CategoryForm(prefix='paano-category', obj=category)
        if current_user.is_authenticated():
            if request.args.get('edit'):
                return render_template('paano/category_form.html', form=form)
            if form.validate_on_submit():
                form.populate_obj(category)
                db.session.add(category)
                db.session.flush()
                flash("Category saved")
                return redirect(category.url())
        return render_template('paano/category.html', category=category,
                               questions=questions)


    @app.route('/categories/<category_id>/questions/<eid>/<title>',
               methods=['GET', 'POST', 'DELETE'])
    def question(category_id, eid, title):
        category = Category.query.get_or_404(category_id)

        selected_lang = request.args.get('lang', DEFAULT_LANG)
        selected_platform = request.args.get('platform', g.detected_platform)
        question = (Question.query
                    .filter_by(category_id=category_id, eid=eid,
                               lang=selected_lang)
                    .filter(Question.platform.in_([DEFAULT_PLATFORM,
                                                   selected_platform]))
                    .first())
        if not question:
            abort(404)

        if current_user.is_authenticated() and request.method == 'DELETE':
            db.session.delete(question)
            return jsonify(success=True)

        form = QuestionForm(prefix='paano-question', obj=question)
        form.category_id.choices = [(c.id, c.title) for c in
                                    Category.query.order_by(Category.title).all()]

        if current_user.is_authenticated():
            if request.args.get('edit'):
                return render_template('paano/question_form.html', form=form)
            if form.validate_on_submit():
                form.populate_obj(question)
                db.session.add(question)
                db.session.flush()
                flash("Question saved")
                return redirect(question.url(platform=question.platform))

        return render_template('paano/question.html', category=category,
                               question=question)


    @app.route('/upload', methods=['GET', 'POST'])
    def upload():
        if request.method == 'POST':
            f = request.files.get('userfile')
            if f:
                # Add a random prefix to keep filenames unique
                filename = (base64.urlsafe_b64encode(os.urandom(9)) + '_' +
                            secure_filename(f.filename))
                # Check if has valid extension
                if any(map(filename.endswith, ALLOWED_EXTS)):
                    # Store
                    f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                    # Return upload path
                    return jsonify(filename=url_for('uploads', filename=filename))

        abort(400)


    @app.route('/uploads/<path:filename>')
    def uploads(filename):
        return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
