# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from math import ceil
import re
from smtplib import SMTPException

from boards.models import Board, Reply
from boards.table import BoardTable
from core.utils import error_page

from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.core.signing import TimestampSigner
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import ugettext as _

from teams.table import TeamTable

from .forms import RegistrationForm, SettingForm, UserInfoForm
from .models import Profile, UserSession


@login_required
def setting(request):
    """Account setting"""
    if request.method == "POST":
        settingform = SettingForm(request.POST)
        if settingform.is_valid():
            setting = settingform.save(commit=False)
            request.user.profile.sense_client = setting.sense_client
            request.user.profile.sense_slot = setting.sense_slot
            request.user.profile.alarm_board = setting.alarm_board
            request.user.profile.alarm_reply = True
            request.user.profile.alarm_paper = setting.alarm_paper
            request.user.profile.alarm_team = setting.alarm_team
            request.user.profile.alarm_full = True
            if setting.alarm_interval < settings.MIN_ALARM_INTERVAL:
                request.user.profile.alarm_interval \
                    = settings.MIN_ALARM_INTERVAL
            elif setting.alarm_interval > settings.MAX_ALARM_INTERVAL:
                request.user.profile.alarm_interval \
                    = settings.MAX_ALARM_INTERVAL
            else:
                request.user.profile.alarm_interval = setting.alarm_interval
            request.user.profile.save()
            msg = _('Saved successfully.')
        else:
            msg = _('Form validation Failure')

    elif request.method == "GET":
        if request.user.is_authenticated:
            msg = ""
            settingform = SettingForm(instance=request.user.profile)
        else:
            return redirect('/')

    return render(
        request,
        "accounts/setting.html",
        {
            'settingform': settingform,
            'msg': msg,
        }
    )


@login_required
def edit_user_info(request):
    """Edit user information"""
    profile = get_object_or_404(Profile, pk=request.user.profile.id)
    if request.method == "POST":
        infoform = UserInfoForm(request.POST, request.FILES, instance=profile)
        if infoform.is_valid():
            error = False
            if settings.ENABLE_NICKNAME:
                nick = infoform.cleaned_data['first_name']
                if nick != request.user.first_name:
                    if nick == '':
                        request.user.first_name = ''
                    else:
                        q = Q(username__iexact=nick) \
                            | Q(first_name__iexact=nick)
                        if User.objects.filter(q).exists() or \
                            len(nick) < settings.NICKNAME_MIN_LENGTH or \
                                len(nick) > settings.NICKNAME_MAX_LENGTH:
                                msg = _('Please check nickname.')
                                error = True
                        else:
                            request.user.first_name = nick

            email = infoform.cleaned_data['email']
            if not error and email != request.user.email:
                code = infoform.cleaned_data['code']
                signer = TimestampSigner()
                try:
                    value = signer.unsign(
                        code, max_age=settings.VERIFICATION_CODE_VALID)
                    code_check = value == email

                    if code_check:
                        request.user.email = email
                    else:
                        msg = _('Verification failure. Please check verification code again.')
                        error = True
                except:
                    msg = _('Verification failure. Please check verification code again.')
                    error = True

            if not error:
                msg = _('Saved successfully.')
                request.user.save()
                infoform.save()
        else:
            msg = _('Form validation Failure')
    elif request.method == "GET":
        if request.user.is_authenticated:
            msg = ""
            infoform = UserInfoForm(instance=profile)
        else:
            return redirect('/')

    return render(
        request,
        "accounts/edit_user_info.html",
        {
            'infoform': infoform,
            'username': request.user.username,
            'date_joined': request.user.date_joined,
            'point': profile.point,
            'portrait': profile.portrait,
            'msg': msg,
        }
    )


@login_required
def user_info(request, user):
    """Show user info"""
    userinfo = User.objects.filter(username__iexact=user).get()
    article_no = Board.objects.filter(user__username__iexact=user).count()
    reply_no = Reply.objects.filter(user__username__iexact=user).count()

    return render(
        request,
        "accounts/user_info.html",
        {
            'userinfo': userinfo,
            'article_no': article_no,
            'reply_no': reply_no,
        }
    )


@login_required
def scrap_list(request, page=0):
    """Show scrap list"""
    if int(page) < 1:
        return redirect('accounts:scrap_list', page=1)

    board_table = BoardTable()
    my_scrap = []
    name_list = board_table.get_table_list()
    list_count = board_table.get_list_count()

    current_page = int(page) - 1
    start_at = current_page * list_count
    end_at = start_at + list_count

    q = Q(status__iexact='1normal') | Q(status__iexact='4warning') \
        | Q(status__iexact='3notice')

    scrap = request.user.profile.scrap.split(',')
    total = len(scrap)

    if request.user.profile.scrap != '':
        for index, s in enumerate(scrap[start_at:end_at]):
            app, id = s.split(':')
            if app == 'boards':
                item = Board.objects.filter(id__iexact=id).filter(q)
                if item.count():
                    my_scrap.append([item[0]])
            else:
                continue

    index_total = int(ceil(float(total) / list_count))
    index_begin = int(current_page / 10) * 10 + 1
    index_end = mindex_end = index_total
    if index_end - index_begin >= 10:
        index_end = index_begin + 9
    mindex_begin = int(current_page / 5) * 5 + 1
    if mindex_end - mindex_begin >= 5:
        mindex_end = mindex_begin + 4

    return render(
        request,
        "accounts/scrap.html",
        {
            'my_scrap': my_scrap,
            'total': total,
            'page': current_page + 1,
            'index_begin': index_begin,
            'index_end': index_end + 1,
            'mindex_begin': mindex_begin,
            'mindex_end': mindex_end + 1,
            'index_total': index_total,
            'name_list': name_list,
        }
    )


@login_required
def delete_scrap(request, id):
    """Delete selected scrap"""
    profile = request.user.profile
    app_id = 'boards:' + id
    regstr = re.escape(app_id) + r"\b(,|)"
    profile.scrap = re.sub(regstr, '', profile.scrap)
    if profile.scrap and profile.scrap[-1] == ',':
        profile.scrap = profile.scrap[:-1]

    request.user.profile.save()
    return redirect('accounts:scrap_list_0')


@login_required
def edit_bookmarks(request):
    """Edit bookmarks"""
    my_bookmark = []
    if request.user.profile.bookmarks:
        bookmarks = request.user.profile.bookmarks.split(',')

        for bm in bookmarks:
            app, id = bm.split('-')
            if app == 'boards':
                app_table = BoardTable()
            elif app == 'teams':
                app_table = TeamTable()
            else:
                continue
            my_bookmark.append(
                [bm, app_table.get_table_name(id)]
            )

    return render(
        request,
        "accounts/edit_bookmarks.html",
        {
            'my_bookmark': my_bookmark,
        }
    )


def sign_up(request):
    """Sign up"""
    if request.method == "POST":
        userform = RegistrationForm(request.POST)
        if userform.is_valid():
            userform.save(commit=False)

            username = userform.cleaned_data['username']
            q = Q(username__iexact=username) | Q(first_name__iexact=username)
            if User.objects.filter(q).exists() or \
                len(username) < settings.ID_MIN_LENGTH or \
                    len(username) > settings.ID_MAX_LENGTH:
                errormsg = _('Please check username.')
                return error_page(request, errormsg)

            if settings.ENABLE_NICKNAME:
                nick = userform.cleaned_data['first_name']
                if nick:
                    q = Q(username__iexact=nick) | Q(first_name__iexact=nick)
                    if User.objects.filter(q).exists() or \
                        len(nick) < settings.NICKNAME_MIN_LENGTH or \
                            len(nick) > settings.NICKNAME_MAX_LENGTH:
                        errormsg = _('Please check nickname.')
                        return error_page(request, errormsg)

            code = userform.cleaned_data['code']
            email = userform.cleaned_data['email']
            signer = TimestampSigner()

            try:
                value = signer.unsign(
                    code, max_age=settings.VERIFICATION_CODE_VALID)
                code_check = value == email

                if code_check:
                    userform.save()
                    return render(
                        request,
                        "accounts/join.html",
                    )
                else:
                    errormsg = _('Verification failure. Please check verification code again.')
            except:
                errormsg = _('Verification failure. Please check verification code again.')
        else:
            errormsg = _('Sorry. Please try again later.')

        return error_page(request, errormsg)
    elif request.method == "GET":
        userform = RegistrationForm()

    return render(
        request,
        "accounts/signup.html",
        {
            'userform': userform,
        }
    )


@login_required
def show_deactivate_account(request):
    """Show deactivate account page"""
    return render(
        request,
        "accounts/deactivate_account.html"
    )


@login_required
def deactivate_account(request):
    """Deactivate account"""
    if request.user.is_authenticated:
        request.user.is_active = False
        if request.user.is_staff:
            request.user.is_staff = False
        request.user.save()

    return redirect(reverse_lazy('accounts:logout'))


@user_passes_test(lambda u: u.is_superuser)
def send_email(request):
    """Send email to user for testing purpose"""
    id_email = request.user.email
    signer = TimestampSigner()
    value = signer.sign(id_email)
    subject = u'Test email.'
    body = u'keyCode: %s' % value

    try:
        send_mail(subject, body, settings.EMAIL_HOST_USER, [id_email], fail_silently=False)
        return error_page(request, "Email sent", status=201)
    except SMTPException:
        return error_page(request, "Error!")


@staff_member_required
def dashboard_user(request, search_word='', condition='recent', page=1):
    """Dashboard user"""
    list_count = settings.DASHBOARD_LIST_COUNT

    if int(page) < 1:
        return redirect('accounts:dashboard_user', condition, 1)

    if condition == 'recent':
        order = '-id'
    elif condition == 'point':
        order = '-profile__point'
    elif condition == 'login':
        order = '-last_login'
    elif condition == 'suspension':
        order = '-profile__suspension_till'
    elif condition != 'default':
        return error_page(request)

    current_page = int(page) - 1
    start_at = current_page * list_count
    end_at = start_at + list_count

    if search_word == '':
        q = Q()
    else:
        q = (Q(username__icontains=search_word) | Q(first_name__icontains=search_word)) | Q(email__icontains=search_word) | Q(profile__ip_list__icontains=search_word)

    total = User.objects.filter(q).count()
    if condition == 'default':
        users = User.objects.filter(q).order_by(
            '-is_superuser', '-is_staff', '-is_active', 'username')[
                start_at:end_at]
    elif condition == 'suspension':
        users = User.objects.filter(q).filter(is_active=False).order_by(
            order)[start_at:end_at]
    else:
        users = User.objects.filter(q).order_by(order)[start_at:end_at]

    index_total = int(ceil(float(total) / list_count))
    index_begin = int(current_page / 10) * 10 + 1
    index_end = mindex_end = index_total
    if index_end - index_begin >= 10:
        index_end = index_begin + 9
    mindex_begin = int(current_page / 5) * 5 + 1
    if mindex_end - mindex_begin >= 5:
        mindex_end = mindex_begin + 4

    return render(
        request,
        "accounts/dashboard_user.html",
        {
            'users': users,
            'total': total,
            'page': current_page + 1,
            'index_begin': index_begin,
            'index_end': index_end + 1,
            'mindex_begin': mindex_begin,
            'mindex_end': mindex_end + 1,
            'index_total': index_total,
            'search_word': search_word,
            'condition': condition,
        }
    )


@staff_member_required
def suspension(request, user, days):
    """Suspend user account for days"""
    sus_days = int(days)
    userinfo = User.objects.filter(username__iexact=user).get()

    if sus_days == 0 and not userinfo.is_active:
        userinfo.profile.suspension_till = timezone.now()
        userinfo.is_active = True
        userinfo.save()
    elif sus_days > 0:
        sus_until = timezone.now() + timezone.timedelta(days=sus_days)
        userinfo.profile.suspension_till = sus_until
        userinfo.is_active = False
        userinfo.save()

        sessions = UserSession.objects.filter(user=userinfo)
        for session in sessions:
            session.session.delete()

    return redirect('accounts:user_info', user)

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from math import ceil
import re
from smtplib import SMTPException

from boards.models import Board, Reply
from boards.table import BoardTable
from core.utils import error_page

from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.core.signing import TimestampSigner
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import ugettext as _

from teams.table import TeamTable

from .forms import RegistrationForm, SettingForm, UserInfoForm
from .models import Profile, UserSession


@login_required
def setting(request):
    """Account setting"""
    if request.method == "POST":
        settingform = SettingForm(request.POST)
        if settingform.is_valid():
            setting = settingform.save(commit=False)
            request.user.profile.sense_client = setting.sense_client
            request.user.profile.sense_slot = setting.sense_slot
            request.user.profile.alarm_board = setting.alarm_board
            request.user.profile.alarm_reply = True
            request.user.profile.alarm_paper = setting.alarm_paper
            request.user.profile.alarm_team = setting.alarm_team
            request.user.profile.alarm_full = True
            if setting.alarm_interval < settings.MIN_ALARM_INTERVAL:
                request.user.profile.alarm_interval \
                    = settings.MIN_ALARM_INTERVAL
            elif setting.alarm_interval > settings.MAX_ALARM_INTERVAL:
                request.user.profile.alarm_interval \
                    = settings.MAX_ALARM_INTERVAL
            else:
                request.user.profile.alarm_interval = setting.alarm_interval
            request.user.profile.save()
            msg = _('Saved successfully.')
        else:
            msg = _('Form validation Failure')

    elif request.method == "GET":
        if request.user.is_authenticated:
            msg = ""
            settingform = SettingForm(instance=request.user.profile)
        else:
            return redirect('/')

    return render(
        request,
        "accounts/setting.html",
        {
            'settingform': settingform,
            'msg': msg,
        }
    )


@login_required
def edit_user_info(request):
    """Edit user information"""
    profile = get_object_or_404(Profile, pk=request.user.profile.id)
    if request.method == "POST":
        infoform = UserInfoForm(request.POST, request.FILES, instance=profile)
        if infoform.is_valid():
            error = False
            if settings.ENABLE_NICKNAME:
                nick = infoform.cleaned_data['first_name']
                if nick != request.user.first_name:
                    if nick == '':
                        request.user.first_name = ''
                    else:
                        q = Q(username__iexact=nick) \
                            | Q(first_name__iexact=nick)
                        if User.objects.filter(q).exists() or \
                            len(nick) < settings.NICKNAME_MIN_LENGTH or \
                                len(nick) > settings.NICKNAME_MAX_LENGTH:
                                msg = _('Please check nickname.')
                                error = True
                        else:
                            request.user.first_name = nick

            email = infoform.cleaned_data['email']
            if not error and email != request.user.email:
                code = infoform.cleaned_data['code']
                signer = TimestampSigner()
                try:
                    value = signer.unsign(
                        code, max_age=settings.VERIFICATION_CODE_VALID)
                    code_check = value == email

                    if code_check:
                        request.user.email = email
                    else:
                        msg = _('Verification failure. Please check verification code again.')
                        error = True
                except:
                    msg = _('Verification failure. Please check verification code again.')
                    error = True

            if not error:
                msg = _('Saved successfully.')
                request.user.save()
                infoform.save()
        else:
            msg = _('Form validation Failure')
    elif request.method == "GET":
        if request.user.is_authenticated:
            msg = ""
            infoform = UserInfoForm(instance=profile)
        else:
            return redirect('/')

    return render(
        request,
        "accounts/edit_user_info.html",
        {
            'infoform': infoform,
            'username': request.user.username,
            'date_joined': request.user.date_joined,
            'point': profile.point,
            'portrait': profile.portrait,
            'msg': msg,
        }
    )


@login_required
def user_info(request, user):
    """Show user info"""
    userinfo = User.objects.filter(username__iexact=user).get()
    article_no = Board.objects.filter(user__username__iexact=user).count()
    reply_no = Reply.objects.filter(user__username__iexact=user).count()

    return render(
        request,
        "accounts/user_info.html",
        {
            'userinfo': userinfo,
            'article_no': article_no,
            'reply_no': reply_no,
        }
    )


@login_required
def scrap_list(request, page=0):
    """Show scrap list"""
    if int(page) < 1:
        return redirect('accounts:scrap_list', page=1)

    board_table = BoardTable()
    my_scrap = []
    name_list = board_table.get_table_list()
    list_count = board_table.get_list_count()

    current_page = int(page) - 1
    start_at = current_page * list_count
    end_at = start_at + list_count

    q = Q(status__iexact='1normal') | Q(status__iexact='4warning') \
        | Q(status__iexact='3notice')

    scrap = request.user.profile.scrap.split(',')
    total = len(scrap)

    if request.user.profile.scrap != '':
        for index, s in enumerate(scrap[start_at:end_at]):
            app, id = s.split(':')
            if app == 'boards':
                item = Board.objects.filter(id__iexact=id).filter(q)
                if item.count():
                    my_scrap.append([item[0]])
            else:
                continue

    index_total = int(ceil(float(total) / list_count))
    index_begin = int(current_page / 10) * 10 + 1
    index_end = mindex_end = index_total
    if index_end - index_begin >= 10:
        index_end = index_begin + 9
    mindex_begin = int(current_page / 5) * 5 + 1
    if mindex_end - mindex_begin >= 5:
        mindex_end = mindex_begin + 4

    return render(
        request,
        "accounts/scrap.html",
        {
            'my_scrap': my_scrap,
            'total': total,
            'page': current_page + 1,
            'index_begin': index_begin,
            'index_end': index_end + 1,
            'mindex_begin': mindex_begin,
            'mindex_end': mindex_end + 1,
            'index_total': index_total,
            'name_list': name_list,
        }
    )


@login_required
def delete_scrap(request, id):
    """Delete selected scrap"""
    profile = request.user.profile
    app_id = 'boards:' + id
    regstr = re.escape(app_id) + r"\b(,|)"
    profile.scrap = re.sub(regstr, '', profile.scrap)
    if profile.scrap and profile.scrap[-1] == ',':
        profile.scrap = profile.scrap[:-1]

    request.user.profile.save()
    return redirect('accounts:scrap_list_0')


@login_required
def edit_bookmarks(request):
    """Edit bookmarks"""
    my_bookmark = []
    if request.user.profile.bookmarks:
        bookmarks = request.user.profile.bookmarks.split(',')

        for bm in bookmarks:
            app, id = bm.split('-')
            if app == 'boards':
                app_table = BoardTable()
            elif app == 'teams':
                app_table = TeamTable()
            else:
                continue
            my_bookmark.append(
                [bm, app_table.get_table_name(id)]
            )

    return render(
        request,
        "accounts/edit_bookmarks.html",
        {
            'my_bookmark': my_bookmark,
        }
    )


def sign_up(request):
    """Sign up"""
    if request.method == "POST":
        userform = RegistrationForm(request.POST)
        if userform.is_valid():
            userform.save(commit=False)

            username = userform.cleaned_data['username']
            q = Q(username__iexact=username) | Q(first_name__iexact=username)
            if User.objects.filter(q).exists() or \
                len(username) < settings.ID_MIN_LENGTH or \
                    len(username) > settings.ID_MAX_LENGTH:
                errormsg = _('Please check username.')
                return error_page(request, errormsg)

            if settings.ENABLE_NICKNAME:
                nick = userform.cleaned_data['first_name']
                if nick:
                    q = Q(username__iexact=nick) | Q(first_name__iexact=nick)
                    if User.objects.filter(q).exists() or \
                        len(nick) < settings.NICKNAME_MIN_LENGTH or \
                            len(nick) > settings.NICKNAME_MAX_LENGTH:
                        errormsg = _('Please check nickname.')
                        return error_page(request, errormsg)

            code = userform.cleaned_data['code']
            email = userform.cleaned_data['email']
            signer = TimestampSigner()

            try:
                value = signer.unsign(
                    code, max_age=settings.VERIFICATION_CODE_VALID)
                code_check = value == email

                if code_check:
                    userform.save()
                    return render(
                        request,
                        "accounts/join.html",
                    )
                else:
                    errormsg = _('Verification failure. Please check verification code again.')
            except:
                errormsg = _('Verification failure. Please check verification code again.')
        else:
            errormsg = _('Sorry. Please try again later.')

        return error_page(request, errormsg)
    elif request.method == "GET":
        userform = RegistrationForm()

    return render(
        request,
        "accounts/signup.html",
        {
            'userform': userform,
        }
    )


@login_required
def show_deactivate_account(request):
    """Show deactivate account page"""
    return render(
        request,
        "accounts/deactivate_account.html"
    )


@login_required
def deactivate_account(request):
    """Deactivate account"""
    if request.user.is_authenticated:
        request.user.is_active = False
        if request.user.is_staff:
            request.user.is_staff = False
        request.user.save()

    return redirect(reverse_lazy('accounts:logout'))


@user_passes_test(lambda u: u.is_superuser)
def send_email(request):
    """Send email to user for testing purpose"""
    id_email = request.user.email
    signer = TimestampSigner()
    value = signer.sign(id_email)
    subject = u'Test email.'
    body = u'keyCode: %s' % value

    try:
        send_mail(subject, body, settings.EMAIL_HOST_USER, [id_email], fail_silently=False)
        return error_page(request, "Email sent", status=201)
    except SMTPException:
        return error_page(request, "Error!")


@staff_member_required
def dashboard_user(request, search_word='', condition='recent', page=1):
    """Dashboard user"""
    list_count = settings.DASHBOARD_LIST_COUNT

    if int(page) < 1:
        return redirect('accounts:dashboard_user', condition, 1)

    if condition == 'recent':
        order = '-id'
    elif condition == 'point':
        order = '-profile__point'
    elif condition == 'login':
        order = '-last_login'
    elif condition == 'suspension':
        order = '-profile__suspension_till'
    elif condition != 'default':
        return error_page(request)

    current_page = int(page) - 1
    start_at = current_page * list_count
    end_at = start_at + list_count

    if search_word == '':
        q = Q()
    else:
        q = (Q(username__icontains=search_word) | Q(first_name__icontains=search_word)) | Q(email__icontains=search_word) | Q(profile__ip_list__icontains=search_word)

    total = User.objects.filter(q).count()
    if condition == 'default':
        users = User.objects.filter(q).order_by(
            '-is_superuser', '-is_staff', '-is_active', 'username')[
                start_at:end_at]
    elif condition == 'suspension':
        users = User.objects.filter(q).filter(is_active=False).order_by(
            order)[start_at:end_at]
    else:
        users = User.objects.filter(q).order_by(order)[start_at:end_at]

    index_total = int(ceil(float(total) / list_count))
    index_begin = int(current_page / 10) * 10 + 1
    index_end = mindex_end = index_total
    if index_end - index_begin >= 10:
        index_end = index_begin + 9
    mindex_begin = int(current_page / 5) * 5 + 1
    if mindex_end - mindex_begin >= 5:
        mindex_end = mindex_begin + 4

    return render(
        request,
        "accounts/dashboard_user.html",
        {
            'users': users,
            'total': total,
            'page': current_page + 1,
            'index_begin': index_begin,
            'index_end': index_end + 1,
            'mindex_begin': mindex_begin,
            'mindex_end': mindex_end + 1,
            'index_total': index_total,
            'search_word': search_word,
            'condition': condition,
        }
    )


@staff_member_required
def suspension(request, user, days):
    """Suspend user account for days"""
    sus_days = int(days)
    userinfo = User.objects.filter(username__iexact=user).get()

    if sus_days == 0 and not userinfo.is_active:
        userinfo.profile.suspension_till = timezone.now()
        userinfo.is_active = True
        userinfo.save()
    elif sus_days > 0:
        sus_until = timezone.now() + timezone.timedelta(days=sus_days)
        userinfo.profile.suspension_till = sus_until
        userinfo.is_active = False
        userinfo.save()

        sessions = UserSession.objects.filter(user=userinfo)
        for session in sessions:
            session.session.delete()

    return redirect('accounts:user_info', user)

# -*- coding: utf-8 -*- Account setting Edit user information Show user info Show scrap list Delete selected scrap Edit bookmarks Sign up Show deactivate account page Deactivate account Send email to user for testing purpose Dashboard user Suspend user account for days