from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.views import APIView, View
from rest_framework.request import Request
from django.views.decorators.csrf import csrf_exempt

from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse, HttpRequest
from django.db.models.query import QuerySet

from django.core.validators import validate_email
from django.core.exceptions import ValidationError

from .models import Apartament, User, PsychTestAnswers, AuthToken
from .serializer import (ApartamentListSerializer, 
                         ApartamentDetailSerializer,
                         PsychTestAddResultSerializer,
                         PublicUserSerializer,
                         PsychTestReultsSerializer,
                         UserSerializer,
                         TokenSerializer)

from .authlib import *

import json, math, random, re, requests, base64, uuid, time, ipware as iplib
ipware = iplib.IpWare();

class ApartamentViewSet(viewsets.ReadOnlyModelViewSet):
    """Вывод списка квартир или отдельной квартиры"""

    def get_queryset(self):
        apartaments = Apartament.objects.all()
        return apartaments
    
    def get_serializer_class(self):
        if self.action == 'list':
            return ApartamentListSerializer
        elif self.action == "retrieve":
            return ApartamentDetailSerializer


class ApartamentGetManyViewSet(viewsets.ReadOnlyModelViewSet):
    """Вывод отдельных квартир для сравнения"""
    def get_queryset(self): # получение id квартир для избранного или сравнения
        pk = self.request.query_params.get("pk", None) # получение id пользователя

        if self.action == "retrieve": # если для страницы сравнения
            apartaments_id = User.objects.get(pk=pk).apartaments_for_comparison.split(',') # получение id квартир
        elif self.action == "list": # если для страницы избранного
            apartaments_id = User.objects.get(pk=pk).favorites_apartaments.split(',') # получение id квартир
        
        queryset = []
        for i in apartaments_id:
            queryset.append(Apartament.objects.get(pk=i))

        return queryset
    
    def retrieve(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        apartaments = []
        for i in queryset:
            apartaments.append(ApartamentDetailSerializer(i).data)
        return Response({'results': apartaments})

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        apartaments = []
        for i in queryset:
            apartaments.append(ApartamentListSerializer(i).data)
        return Response({'results': apartaments})


class ApartmentFilter(viewsets.ViewSet):
    """Вывод списка квартир или отдельной квартиры"""

    def list(self, req: Request):
        filters = dict(req.data);
        unfiltered = Apartament.objects.all();
        filtered = [];

        # TODO: Some better converting practice?
        filters['price_range']['from'] = int(filters['price_range']['from']);
        filters['price_range']['to'] = int(filters['price_range']['to']);
        filters['area_range']['from'] = int(filters['area_range']['from']);
        filters['area_range']['to'] = int(filters['area_range']['to']);
        filters['rooms'] = int(filters['rooms'])

        for entry in unfiltered:
            if (filters['price_range']['from'] >= entry.price and (filters['price_range']['to'] != -1 and filters['price_range']['to'] <= entry.price)):
                continue;
            if (filters['area_range']['from'] >= entry.perimetrs and (filters['area_range']['to'] != -1 and filters['area_range']['to'] <= entry.perimetrs)):
                continue;
            if (filters['rooms'] != -1 and entry.rooms != filters['rooms']):
                continue;
            
            filtered.append(entry);

        return Response(ApartamentListSerializer(filtered, many=True).data);


class PsychTestAddResultViewSet(viewsets.ViewSet):
    def get_object(self, pk):
        return User.objects.get(pk=pk)
    def create(self, request, pk):
        user = self.get_object(pk)
        results = request.data
        PsychTestAnswers.objects.create(
            user=user,
            first_question=results[0],
            second_question=results[1],
            third_question=results[2],
            fourth_question=results[3],
            fifth_question=results[4],
            sixth_question=results[5],
            seventh_question=results[6],
            eighth_question=results[7],
            nineth_question=results[8],
            tenth_question=results[9],
            eleventh_question=results[10],
            twelfth_question=results[11]
        )
        return Response({'successfully': 'results post'})


class CompatibleUsersView(viewsets.ViewSet):
    @rest_auth_required
    def list(self, req: Request):
        user_data = dict(req.data);
        
        token = AuthToken.objects.get(key=req.headers['authorization']);

        this_user = User.objects.get(pk=token.user);
        
        answers_this_user = None;
        try:
            answers_this_user = PsychTestReultsSerializer(PsychTestAnswers.objects.get(user=this_user.id)).dict;
        except PsychTestAnswers.DoesNotExist:
            return Response({'error': 'answers not found'}, 404);

        users_answers_query = PsychTestReultsSerializer(PsychTestAnswers.objects.all(), many=True).dict

        users = [];

        for user_answers in users_answers_query:
            score = 0
            for i in range(1, 12):
                if answers_this_user[i] == user_answers[i]:
                    score += 1
            if score / 12 * 100 > 30:
                users.append(UserSerializer(User.objects.get(pk=user_answers[0])).data)
        

        return Response(users);

class UserLogin(APIView):

    # TODO: Remove csrf exempt when index.html is loaded through django
    @csrf_exempt
    def post(self, req: HttpRequest):

        # for debug purposes
        # return HttpResponse("""{"user_data": {"id": 1, "name": "\u041d\u0438\u043a\u0438\u0442\u0430 \u041f\u0443\u0441\u0442\u043e\u0432\u0430\u043b\u043e\u0432", "date_of_birth": null, "about_me": "", "gender": "?", "phone": "+00000", "email": null, "telegram": null, "discord": null, "city": null, "role": "s", "photo_provider": "VVSU", "openid_addr": "blek__@vvsu.ru", "openid_id": "096C78CD-4943-4D57-BC6D-5CDE12F686E3"}, "new_user": false, "token": {"id": 2, "user": 1, "key": "e1c24581-523a-4f60-973f-02ba873b3edc", "expires": 1684423572, "ip": "127.0.0.1"}}""");

        if (req.session.has_key('auth_data')):
            # TODO: Return user object instead of error
            return JsonResponse({'error': 'already authenticated'})

        if (req.content_type != 'application/json'):
            res = HttpResponse({'error': 'bad content type'});
            res.status_code = 400;
            return res;

        data = json.loads(req.body.decode('utf8'));

        if not ('code' in data and 'code_verifier' in data):
            res = JsonResponse({'error': 'no code'});
            res.status_code = 400;
            return res;

        auth_data = get_oauth_token('https://vvsu.ru/connect', {
            'grant_type': 'authorization_code',
            'redirect_uri': 'https://pairent.vvsu.ru/sign-in/',
            'code': data['code'],
            'code_verifier': data['code_verifier'],
            'client_id': 'it-hub-client',
            'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd'
        });

        if ('error' in auth_data):
            return JsonResponse(auth_data);

        user = None;
        new_user = False;
        
        vvsu_data = get_oauth_data('https://vvsu.ru/connect', auth_data['access_token']);
        
        if ('error' in vvsu_data):
            res = JsonResponse(vvsu_data);
            res.status_code = 500;
            return res;

        if ('error' in vvsu_data):
            res = JsonResponse(vvsu_data);
            res.status_code = 500;
            return res

        vvsu_data['vvsu_login'] += '@vvsu.ru';
        try:
            user = User.objects.get(openid_addr=vvsu_data['vvsu_login']);
        except User.DoesNotExist:
            user = register(vvsu_data['vvsu_login'], vvsu_data['id'], f"{vvsu_data['given_name']} {vvsu_data['family_name']}");
            new_user = True;
        
        return JsonResponse({
            'user_data': PublicUserSerializer(user).data,
            'new_user': new_user,
            'token': TokenSerializer(create_auth_token(user.id, ipware.get_client_ip(req.META)[0].exploded)).data
        });

class UserGet(APIView):
    def get(self, req: HttpRequest):
        if not ('id' in req.GET.keys() or 'login' in req.GET.keys()):
            res = JsonResponse({'error': 'no id or login'});
            res.status_code = 400;
            return res;
        
        id_type = 'id' if 'id' in req.GET.keys() else 'login';
        id = req.GET.get(id_type);

        if (id_type == 'login'):
            if not id.endswith('@vvsu.ru'):
                id += '@vvsu.ru';
            id_type = 'openid_addr';

        user = None;
        try:
            user = User.objects.get(**{id_type: id});
        except User.DoesNotExist:
            res = JsonResponse({'error': 'not found'});
            res.status_code = 404;
            return res;
        
        return JsonResponse(PublicUserSerializer(user).data);


def VVSUAuthProxy(req: HttpRequest):
    proxy = 'https://vvsu.ru/connect' + req.path[len('/api/auth/vvsu'):];
    
    preq = requests.request(req.method, proxy, headers={
        'User-Agent': 'OIDC Client / Pairent',
        'Origin': 'http://pairent.vvsu.ru',
        'Referer': 'http://pairent.vvsu.ru'
    });

    resp = HttpResponse(preq.content);
    resp.headers['Content-Type'] = preq.headers['Content-Type'];

    return resp;