<reponame>theskinnycoder/dunk
from operator import not_
from django.http.response import HttpResponse
from django.shortcuts import redirect, render
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login
from ratelimit.decorators import ratelimit
from django.db.models import Q

from .forms import UserRegistrationForm, UserUpdateForm, ProfileUpdateForm
from .models import Profile, User, Friend


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
def register(request):
    if request.method == 'POST':
        u_form = UserRegistrationForm(request.POST)
        p_form = ProfileUpdateForm(request.POST, request.FILES)
        if u_form.is_valid() and p_form.is_valid():
            user = u_form.save()
            profile = p_form.save(commit=False)
            profile.user = user
            profile.save()
            user = authenticate(
                request, username=u_form.cleaned_data['username'], password=u_form.cleaned_data['<PASSWORD>'])
            login(request, user)
            return redirect('home')
    else:
        u_form = UserRegistrationForm()
        p_form = ProfileUpdateForm()
    context = {
        'u_form': u_form,
        'p_form': p_form
    }
    return render(request, 'users/register.html', context)


@login_required
def profile(request, pk):
    requested_user_profile = Profile.objects.get(pk=pk)
    friends = Friend.objects.filter(
        Q(profile_1=requested_user_profile) | Q(profile_2=requested_user_profile)).select_related('profile_1', 'profile_2').all()
    friends = [friend.profile_1 if friend.profile_1 !=
               requested_user_profile else friend.profile_2 for friend in friends]
    logged_in_user_friends = Friend.objects.filter(
        Q(profile_1=request.user.profile) | Q(profile_2=request.user.profile)).select_related('profile_1', 'profile_2').all()
    logged_in_user_friends = [friend.profile_1 if friend.profile_1 !=
                              request.user.profile else friend.profile_2 for friend in logged_in_user_friends]
    profiles = Profile.objects.all()
    not_friends = []
    count = 0
    for profile in profiles:
        if count == 5:
            break
        if not Friend.objects.filter(
                Q(profile_1=request.user.profile, profile_2=profile) |
                Q(profile_2=request.user.profile, profile_1=profile)).exists():
            not_friends.append(profile)
            count += 1
    not_friends = [
        profile for profile in not_friends if profile != request.user.profile]
    context = {
        'profile': requested_user_profile,
        'friends': friends,
        'profiles': not_friends,
        'logged_in_user_friends': logged_in_user_friends,
    }
    return render(request, 'screens/profile.html', context)


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
@login_required
def update_profile(request):
    if request.method == 'POST':
        profile = request.user.profile
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form = ProfileUpdateForm(
            request.POST, request.FILES, instance=profile)
        if u_form.is_valid() and p_form.is_valid():
            u_form.save()
            p_form.save()
            return redirect('profile', pk=request.user.profile.pk)
    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form = ProfileUpdateForm(instance=request.user.profile)

    context = {
        'u_form': u_form,
        'p_form': p_form,
    }

    return render(request, 'screens/update_profile.html', context)


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
@login_required
def add_friend(request, pk):
    requested_user_profile = Profile.objects.get(pk=pk)
    Friend.objects.create(profile_1=request.user.profile,
                          profile_2=requested_user_profile)
    friends = Friend.objects.filter(
        Q(profile_1=requested_user_profile) | Q(profile_2=requested_user_profile)).select_related('profile_1', 'profile_2').all()
    friends = [friend.profile_1 if friend.profile_1 !=
               requested_user_profile else friend.profile_2 for friend in friends]
    logged_in_user_friends = Friend.objects.filter(
        Q(profile_1=request.user.profile) | Q(profile_2=request.user.profile)).select_related('profile_1', 'profile_2').all()
    logged_in_user_friends = [friend.profile_1 if friend.profile_1 !=
                              request.user.profile else friend.profile_2 for friend in logged_in_user_friends]
    context = {
        'profile': requested_user_profile,
        'friends': friends,
        'logged_in_user_friends': logged_in_user_friends,
    }
    return render(request, 'components/profile_details.html', context)


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
@login_required
def remove_friend(request, pk):
    requested_user_profile = Profile.objects.get(pk=pk)
    Friend.objects.filter(
        Q(profile_1=request.user.profile, profile_2=requested_user_profile) |
        Q(profile_2=request.user.profile, profile_1=requested_user_profile)).delete()
    friends = Friend.objects.filter(
        Q(profile_1=requested_user_profile) | Q(profile_2=requested_user_profile)).select_related('profile_1', 'profile_2').all()
    friends = [friend.profile_1 if friend.profile_1 !=
               requested_user_profile else friend.profile_2 for friend in friends]
    logged_in_user_friends = Friend.objects.filter(
        Q(profile_1=request.user.profile) | Q(profile_2=request.user.profile)).select_related('profile_1', 'profile_2').all()
    logged_in_user_friends = [friend.profile_1 if friend.profile_1 !=
                              request.user.profile else friend.profile_2 for friend in logged_in_user_friends]
    context = {
        'profile': requested_user_profile,
        'friends': friends,
        'logged_in_user_friends': logged_in_user_friends,
    }
    return render(request, 'components/profile_details.html', context)


@ratelimit(key='ip', rate='200/m', method=ratelimit.UNSAFE, block=True)
@login_required
def search(request):
    query = request.POST.get('search')
    profiles = Profile.objects.filter(display_name__icontains=query)
    users = User.objects.filter(username__icontains=query)
    results = list(set(profiles) | set(map(lambda x: x.profile, users)))
    return render(request, "screens/search.html", {"profiles": results})


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
@login_required
def friends(request, pk):
    requested_user_profile = Profile.objects.get(pk=pk)
    friends = Friend.objects.filter(
        Q(profile_1=requested_user_profile) | Q(profile_2=requested_user_profile)).select_related('profile_1', 'profile_2').all()
    friends = [friend.profile_1 if friend.profile_1 !=
               requested_user_profile else friend.profile_2 for friend in friends]
    context = {
        'friends': friends,
        'requested_user': requested_user_profile
    }
    return render(request, "screens/friends.html", context)


@ratelimit(key='ip', rate='200/m', method=ratelimit.UNSAFE, block=True)
def check_username(request):
    username = request.POST.get('username')
    if len(username) < 5:
        return HttpResponse("<div class='font-italic font-bold text-red-500'>Username must be at least 5 characters long</div>")
    if User.objects.filter(username=username).exists():
        return HttpResponse("<div class='font-italic font-bold text-red-500'>This username is already taken</div>")
    else:
        return HttpResponse("<div class='font-italic font-bold text-green-500'>This username is available</div>")


@ratelimit(key='ip', rate='10/m', method=ratelimit.UNSAFE, block=True)
def recent_profiles(request):
    profiles = Profile.objects.all()
    not_friends = []
    count = 0
    for profile in profiles:
        if count == 5:
            break
        if not Friend.objects.filter(
                Q(profile_1=request.user.profile, profile_2=profile) |
                Q(profile_2=request.user.profile, profile_1=profile)).exists():
            not_friends.append(profile)
            count += 1
    not_friends = [
        profile for profile in not_friends if profile != request.user.profile]
    context = {
        'profiles': not_friends,
    }
    return render(request, "components/recent_profiles.html", context)
