from flask import flash, redirect, url_for, request
from flask_login import current_user
from wtforms import BooleanField
from app.utils.priv_required import priv_required
from app.models.user import Guest, Member
from app.models.trophy import Trophy, Title
from app.models.priv import Group
from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \
    AdminAccountEditTrophyForm, AdminAccountEditGroupForm
from app.utils.render import render
from app.utils.notify import notify
from app import app, db
from config import V5Config


@app.route('/admin/compte/<user_id>/editer', methods=['GET', 'POST'])
@priv_required('misc.admin-panel', 'edit.accounts')
def adm_edit_account(user_id):
    user = Member.query.filter_by(id=user_id).first_or_404()

    form = AdminUpdateAccountForm(prefix="user")

    class TrophyForm(AdminAccountEditTrophyForm):
        pass
    class GroupForm(AdminAccountEditGroupForm):
        pass

    for t in Trophy.query.all():
        setattr(TrophyForm, f't{t.id}', BooleanField(t.name))
    setattr(TrophyForm, "trophies", {f't{t.id}': t for t in Trophy.query.all()})
    setattr(TrophyForm, "user_trophies", [f't{t.id}' for t in user.trophies])
    trophy_form = TrophyForm(prefix="trophies")

    for g in Group.query.all():
        setattr(GroupForm, f'g{g.id}', BooleanField(g.name))
    setattr(GroupForm, "groups", {f'g{g.id}': g for g in Group.query.all()})
    setattr(GroupForm, "user_groups", [f'g{g.id}' for g in user.groups])
    group_form = GroupForm(prefix="group")

    titles = [(t.id, t.name) for t in Title.query.all()]
    titles.insert(0, (-1, "Membre"))
    form.title.choices = titles

    if form.submit.data:
        if form.validate_on_submit():
            newname = form.username.data
            names = list(Member.query.filter(Member.id != user.id).values(Member.name))
            if newname in names:
                # TODO: avoid this exception
                # You cannot user vd.name_available because name will always be
                # invalid! Maybe you can add another validator with arguments
                raise Exception(f'{newname} is not available')
            user.update(
                avatar=form.avatar.data or None,
                name=form.username.data or None,
                email=form.email.data or None,
                email_confirmed=form.email_confirmed.data,
                password=form.password.data or None,
                birthday=form.birthday.data,
                signature=form.signature.data,
                title=form.title.data,
                bio=form.biography.data,
                newsletter=form.newsletter.data,
                xp=form.xp.data or None,
            )
            db.session.merge(user)
            db.session.commit()
            # TODO: send an email to member saying his account has been modified
            user.notify(f"Vos informations personnelles ont été modifiées par {current_user.name}.")
            flash('Modifications effectuées', 'ok')
            return redirect(request.url)
        else:
            flash('Erreur lors de la modification', 'error')

    # Trophies
    if trophy_form.submit.data:
        if trophy_form.validate_on_submit():
            for id, field in trophy_form.__dict__.items():
                if id[0] == "t":
                    if field.data:
                        user.add_trophy(int(id[1:]))
                    else:
                        user.del_trophy(int(id[1:]))

            db.session.merge(user)
            db.session.commit()
            flash('Modifications effectuées', 'ok')
            return redirect(request.url)
        else:
            flash("Erreur lors de la modification des trophées", 'error')

    # Groups
    if group_form.submit.data:
        if group_form.validate_on_submit():
            for id, field in group_form.__dict__.items():
                if id[0] == "g":
                    if field.data:
                        user.add_group(int(id[1:]))
                    else:
                        user.del_group(int(id[1:]))

            db.session.merge(user)
            db.session.commit()
            flash('Modifications effectuées', 'ok')
            return redirect(request.url)
        else:
            flash("Erreur lors de la modification des groupes", 'error')

    trophies_owned = set()
    for t in user.trophies:
        trophies_owned.add(f"t{t.id}")

    groups_owned = set()
    for g in user.groups:
        groups_owned.add(f"g{g.id}")

    return render('admin/edit_account.html', scripts=["+scripts/entropy.js"],
        user=user, form=form, trophy_form=trophy_form,
        trophies_owned=trophies_owned, group_form=group_form,
        groups_owned=groups_owned)


@app.route('/admin/compte/<user_id>/supprimer', methods=['GET', 'POST'])
@priv_required('misc.admin-panel', 'delete.accounts')
def adm_delete_account(user_id):
    # A user deleting their own account will be disconnected
    user = Member.query.filter_by(id=user_id).first_or_404()

    # TODO: Number of comments by *other* members which will be deleted
    stats = {
        'comments': len(user.comments()),
        'topics':   len(user.topics()),
        'programs': len(user.programs()),
        'groups':   len(user.groups),
        'privs':    len(user.special_privileges()),
    }

    del_form = AdminDeleteAccountForm()
    if del_form.submit.data:
        if del_form.validate_on_submit():
            if del_form.transfer.data:
                guest = Guest(user.generate_guest_name())
                db.session.add(guest)
                db.session.commit()
                user.transfer_posts(guest)
                db.session.commit()
            else:
                user.delete_posts()
                db.session.commit()

            user.delete()
            db.session.commit()
            flash('Compte supprimé', 'ok')
            return redirect(url_for('adm'))
        else:
            flash('Erreur lors de la suppression du compte', 'error')
            del_form.delete.data = False  # Force to tick to delete the account

    return render('admin/delete_account.html', user=user, stats=stats,
        del_form=del_form)
