from PyPDF2 import PdfFileReader, PdfFileMerger, PdfFileWriter

from datetime import datetime
from datetime import timedelta
from xhtml2pdf import pisa
from django.shortcuts import render

# Create your views here.
from rest_framework import status
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.generics import CreateAPIView
from rest_framework.generics import ListAPIView
from rest_framework.generics import RetrieveAPIView
from rest_framework.generics import DestroyAPIView
from rest_framework.generics import UpdateAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
import requests
from participant import serializers
from participant.api import ParticipantApi, ParticipantStudyConsentApi
from participant.models import Participant
from participant.models import ParticipantStudyConsent
from participant.serializers import ParticipantSerializer, ParticipantStudyOptOutSerializer
from participant.serializers import ParticipantStudyConsentSerializer
from security.api import StudyTokenApi
from survey.utils import response_standard
from survey.api import ObservationApi
from survey.models import Observation
from django.http import HttpResponse
from django.views.generic import View,TemplateView
from django.db import connection

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

from django.template.loader import get_template
from django.template import Context
from django.conf import settings

from django.utils import timezone

import random
import string
import hashlib
from django.template.loader import render_to_string
from django.conf import settings
from django.core.mail import EmailMessage
from django.core.urlresolvers import reverse
from StringIO import StringIO
import json
import csv

from .models import ParticipantAuth

import logging
logger = logging.getLogger(__name__)

class ParticipantInsightsNoopView(APIView):
    def get(self, request):
        return Response(response_standard({"sucessfull"}),
                    status=status.HTTP_200_OK)

class ParticipantStudyView(APIView):
    serializer_class = ParticipantSerializer

    def post(self, request, *args, **kwargs):

        email = request.data.get("email")
        participant = ParticipantApi()._get(email=email)
        if participant:
            return Response({"success": False, "error": "participant exsists"}, status=status.HTTP_400_BAD_REQUEST)
        serializer = self.serializer_class(data=request.data, context={})
        if serializer.is_valid():
            serializer.save()
            return Response(response_standard({"id": serializer.data.get("id"), "uuid": serializer.data.get("uuid")}),
                            status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def put(self, request, *args, **kwargs):
        instance = request.participant_token.participant
        data = request.data
        serializer = self.serializer_class(instance, data=data, context={}, partial=False)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(response_standard({"id": serializer.data.get("id")}))


    def get(self, request):
        try:
            participant = request.participant_token.participant
            serializer = ParticipantSerializer(participant)
            return Response(serializer.data)
        except:
            return Response(response_standard({"error": "Participant doesn't exsist"}))


class ConsentView(CreateAPIView, ListAPIView, UpdateAPIView):
    """
    This view supports creation of only participant and listing all participants
    """
    serializer_class = ParticipantStudyConsentSerializer

    def get_serializer_class(self):
        if self.request.method == "PUT":
            return ParticipantStudyOptOutSerializer
        return ParticipantStudyConsentSerializer

    def get_queryset(self):
        return ParticipantStudyConsent.objects.all()

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        # todo get study from kwargs or studytoken self.kwargs
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self,
        }

    def put(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False),
        opt_out = request.data["opt_out"]
        instance = request.participant_token.participant
        if not instance:
            return Response("id not found", status=status.HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance,  partial=partial)
        serializer.update(participant_obj=instance, opt_out=opt_out)
        return Response(response_standard({"modified": "sucessfuly modified"}),
                         status=status.HTTP_200_OK)

    def post(self, request, *args, **kwargs):
        try:
            serializer = self.serializer_class(data=request.data)
            if serializer.is_valid():
                consent = serializer.create(request)
                self.send_verification_email(consent)
                return Response(response_standard({}),
                                status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            print e

    def sent_verify(self, consent, verify_url):
        ctx = {
            "consent": consent,
            "current_site": settings.SITE_DOMAIN,
            "verify_url": verify_url
        }
        subject = render_to_string("email/verify_subject.txt", ctx)
        subject = "".join(subject.splitlines())
        message = render_to_string("email/verify_body.txt", ctx)
        msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [consent.participant.email])
        msg.content_subtype = "html"
        msg.send()

    def send_verification_email(self, consent):
        token = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(0, 20))
        ParticipantApi()._update(participant_id=consent.participant_id,
                                 email_verification_code=token)

        activation_link = settings.SITE_DOMAIN + reverse('email_verify',
                                                         kwargs=dict(token=token))
        self.sent_verify(consent, activation_link)
        return token


    def send_confirmation_email(self, participant, pdf):
        token = ""
        subject = render_to_string("email/consent_subject.txt")
        subject = "".join(subject.splitlines())
        message = render_to_string("email/consent_body.txt")
        msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [participant.email])
        attachment = open(pdf, 'rb')
        msg.attach('consent.pdf', attachment.read(), 'application/pdf')
        msg.content_subtype = "html"
        msg.send()
        return token


class ParticipantConsentView(APIView):
    def get_queryset(self):
        return Participant.objects.all()

    def get(self, request):
        serializer = ParticipantSerializer(self.get_queryset(), many=True)
        return Response(serializer.data)

    def post(self, request):
        data = request.data
        error = {}
        participant_id, participant_errors = self.participant(data)
        error.update(participant_errors)
        particpantconsent_errors = self.particpantconsent(data, participant_id)
        error.update(particpantconsent_errors)
        device_errors = self.device(data, participant_id)
        error.update(device_errors)
        if not error and participant_id:
            # todo login token
            return Response({"participant_id": participant_id}, status=status.HTTP_201_CREATED)
        return Response(error, status=status.HTTP_406_NOT_ACCEPTABLE)

    def participant(self, data):
        participant_id = None
        errors = {}
        participantserializer = ParticipantSerializer(data=data)
        if participantserializer.is_valid():
            participantserializer.save()
            participant_id = participantserializer.data['id']
        else:
            participant_errors = participantserializer.errors
            errors.update(participant_errors)
        return participant_id, errors

    def particpantconsent(self, data, participant_id):
        errors = {}
        consentserializer = ParticipantStudyConsentSerializer(data=data, context={'participant_id': participant_id})
        if consentserializer.is_valid():
            consentserializer.save()
        else:
            consent_errors = consentserializer.errors
            errors.update(consent_errors)
        return errors

    def device(self, data, participant_id):
        errors = {}
        deviceserializer = DeviceSerializer(data=data, context={'participant_id': participant_id})
        if deviceserializer.is_valid():
            deviceserializer.save()
        else:
            device_errors = deviceserializer.errors
            errors.update(device_errors)
        return errors





class ConsentsView(RetrieveAPIView, DestroyAPIView, UpdateAPIView):
    """
    This view supports creation of only participant and listing all participants
    """
    serializer_class = ParticipantStudyConsentSerializer
    lookup_field = "pk"

    def get_queryset(self):
        return ParticipantStudyConsent.objects.all()

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        # todo get study from kwargs or studytoken self.kwargs
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self,
            'participant_id': 5,
        }

class DownloadConcentPdfView(View):

    def get(self, request, *args, **kwargs):
        participant_id = request.participant_token.participant_id
        response = HttpResponse(content_type="application/pdf")
        # try:
        part_consent_obj = ParticipantStudyConsentApi()._get(participant_id=participant_id)
        consent_file = self.merge_pdf(part_consent_obj)
        outputStream = file(consent_file, "r")
        response.write(outputStream.read())
        outputStream.close()
        # except:
        #     # todo add default consent doc
        #     response = HttpResponse("we can read a  default consent doc", content_type='application/pdf')
        response['Content-Disposition'] = 'inline;filename=consent.pdf'
        return response

    def merge_pdf(self, part_consent_obj):
        def html_to_pdf(template_name, output, data):
            template = get_template(template_name)
            html = template.render(Context(data))
            filew = open(output, "w+b")
            pisaStatus = pisa.CreatePDF(html.encode('utf-8'), dest=file(output, "wb"),
                                        encoding='utf-8')

            filew.seek(0)
            filew.close()

        final_destination = "/tmp/MyNewOutput_" + str(part_consent_obj.participant.uuid) + ".pdf"
        consent_pdf = '/tmp/consent_make' + str(part_consent_obj.participant.uuid) + '.pdf'
        voice_pdf = '/tmp/voice_make'  + str(part_consent_obj.participant.uuid) + '.pdf'
        investigator_pdf = '/tmp/investigator_make'  + str(part_consent_obj.participant.uuid) + '.pdf'


        html_to_pdf('consent.html', consent_pdf, {
            'first_name': part_consent_obj.participant.first_name,
            'last_name': part_consent_obj.participant.last_name,
            'signature_image': part_consent_obj.signature,
            'created_on': part_consent_obj.participant.created_on.strftime("%m-%d-%Y"),
            'consent_obj': part_consent_obj
        })

        html_to_pdf('investigator_date.html', investigator_pdf, {
            'created_on': part_consent_obj.participant.created_on.strftime("%m-%d-%Y"),
        })

        try:
            allow_voice = "Yes" in ObservationApi()._filter(question_survey=208).first().str_value
        except:
            allow_voice = False

        html_to_pdf('voice_record.html', voice_pdf, {'agree': allow_voice})

        output = PdfFileWriter()
        input1 = PdfFileReader(file("{0}/participant/static/consent.pdf".format(settings.BASE_DIR), "rb"))
        input2 = PdfFileReader(file(consent_pdf, "rb"))
        input3 = PdfFileReader(file(voice_pdf, "rb"))
        input4 = PdfFileReader(file(investigator_pdf, "rb"))
        input1.getPage(input1.getNumPages()-2).mergePage(input2.getPage(0))
        input1.getPage(2).mergePage(input3.getPage(0))
        input1.getPage(input1.getNumPages()-1).mergePage(input4.getPage(0))

        # Write PDF
        for page in range(input1.getNumPages()):
            output.addPage(input1.getPage(page))
            outputStream = file(final_destination, "wb")
            output.write(outputStream)
            outputStream.close()

        return final_destination

class ParticipantCodeView(APIView):
    def get(self, request):
        data = request.GET
        code = data.get("code")
        participant = ParticipantApi()._get(reset_code=code)
        if not participant:
            return Response({"success": False, "error": "Invalid Code"}, status=status.HTTP_404_NOT_FOUND)
        time_diff = timezone.now() - participant.reset_code_created_date
        if time_diff.total_seconds() > 86400:
            return Response({"success": False, "error": "code expired"}, status=status.HTTP_400_BAD_REQUEST)

        data = {"id": int(participant.id),
                "uuid": participant.uuid,
                "first_name": participant.first_name,
                "last_name": participant.last_name,
                "email": participant.email}
        logger.debug("Participant already registered: ")
        logger.debug(data)
        return Response(data, status=status.HTTP_200_OK)

    def post(self, request):
        data = request.data
        email = data.get("email")
        participant = ParticipantApi()._get(email=email)
        if not participant:
            return Response({"success": False}, status=status.HTTP_404_NOT_FOUND)
        code = ParticipantApi()._updatecode(email=email)
        self.sendemail_to_participant(email, code)
        logger.debug("in ParticipantCodeView post sent email")
        return Response({"success": True}, status=status.HTTP_200_OK)

    def sendemail_to_participant(self, email, code):
        # todo need to email languages and from address as per app
        subject = render_to_string("email/reset_subject.txt")
        subject = "".join(subject.splitlines())
        message = render_to_string("email/reset_body.txt", {"code": code})
        msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [email])
        msg.content_subtype = "html"
        msg.send()
        return

class EmailVerifyTokenView(TemplateView):
    template_name_fail = "invitation_signup_token_fail.html"
    template_name_success = "invitation_signup_token_success.html"

    def get(self, request, *args, **kwargs):
        success, participant = self.check_token(kwargs["token"])
        if success:

            if "sendpdf" in request.GET:
                show_download_button = False
                part_consent = ParticipantStudyConsentApi()._get(participant_id=participant.id)
                pdf = DownloadConcentPdfView().merge_pdf(part_consent_obj=part_consent)
                ConsentView().send_confirmation_email(participant, pdf)
                ParticipantApi()._update(participant_id=participant.id, consent_doc_emailed=True)
            else:
                show_download_button = True
            response_kwargs = {
                "request": request,
                "template": self.template_name_success,
                "context": {"show_download_button": show_download_button}
            }
            return self.response_class(**response_kwargs)
        else:
            return self.token_fail()

    def check_token(self, token):
        partcipant = ParticipantApi()._get(email_verification_code=token)
        if partcipant:
            return True, partcipant
        return False, None

    def token_fail(self):
        response_kwargs = {
            "request": self.request,
            "template": self.template_name_fail,
            "context": {}
        }
        return self.response_class(**response_kwargs)

class ParticipantTotalCount(View):
    def get(self, request, *args, **kwargs):
        participants = Participant.objects.all().count()
        moments = ObservationApi()._filter(parent=False)
        return HttpResponse(json.dumps({"success": True, "data":
            {
                "total": participants
            }
        }))

class WERFLinkGeneratorView(View):

    template_name = "werf_generator.html"

    def get(self, request, *args, **kwargs):
        if request.user.is_superuser == False:
            return HttpResponse('Access Denied - Login Required')
        if 'export' in request.GET:
            response = HttpResponse(content_type="text/csv; charset=utf-8", )
            response['Content-Disposition'] = 'attachment; filename="werf_exported_users.csv"'
            writer = csv.writer(response)
            for user in Participant.objects.filter(werf_url=None):
                writer.writerow([user.email, "{0} {1}".format(user.first_name.encode('utf-8'), user.last_name.encode('utf-8'))])
            return response
        else:
            return render(request, 'werf_generator.html', {
                'error': kwargs['error'] if 'error' in kwargs and kwargs["error"] != False else None,
                'count': kwargs['count'] if 'count' in kwargs else None
            })

    def post(self, request):
        file = request.FILES["werf_file"]
        reader = csv.reader(file)
        errors = []
        count = 0
        for line in reader:
            try:
                participant = Participant.objects.get(email=line[0])
                participant.werf_url = line[1]
                participant.save()
                count += 1
            except Exception, e:
                errors.append('{0} Email not saved Error : {1}'.format(line[0], e))
        kwargs = {
            'count': count
        }
        if len(errors) > 0:
            kwargs["error"] = "<br />".join(errors)
        return self.get(request, **kwargs)

class CitizenStatsView(View):

    def get(self, request):
        participant_id = request.participant_token.participant_id
        moment_survey_ids = [1, 4, 6, 7, 8, 25, 401]
        total_participants = Participant.objects.all().count()
        total_moments = Observation.objects.filter(question_survey_id__in=moment_survey_ids).filter(is_edited=0).count()
        total_days = 0
        participant_days = 0
        participant_moments = 0
        with connection.cursor() as cursor:
            cursor.callproc('GetTotalDaysTracked', [total_days])
            cursor.execute('SELECT @_GetTotalDaysTracked_0')
            total_days = cursor.fetchone()[0]

            cursor.callproc('GetParticipantDaysTracked', [participant_id, total_days])
            cursor.execute('SELECT @_GetParticipantDaysTracked_1')
            participant_days = cursor.fetchone()[0]

            cursor.callproc('GetParticipantMomentsTrackedToday', [participant_id, total_moments])
            cursor.execute('SELECT @_GetParticipantMomentsTrackedToday_1')
            participant_moments = cursor.fetchone()[0]
            
        data = {
            'participant_moments': participant_moments,
            'participant_days': participant_days,
            'total_participants':total_participants,
            'total_moments': total_moments,
            'total_days': total_days
        }
        
        response = {"success": True, "data": data}
        date = datetime.now()
        return HttpResponse(json.dumps(response))

class ParticipantExternalAuthorization(View):

    # - UPDATE AUTH TABLE ALWAYS AND USE NEW DATE

    def get(self, request):
        participant_id = request.participant_token.participant_id
        participant = ParticipantApi()._get(id=participant_id)
        expire = datetime.now() + timedelta(days=1)
        try:
            auth = ParticipantAuth.objects.get(participant=participant)
            auth.delete()
        except ParticipantAuth.DoesNotExist:
            pass
        while True:
            choices = list(string.ascii_uppercase) + list(string.digits)
            for x in ['L', 'I', 'O', '0']:
                choices.remove(x)
            token = ''.join(random.choice(choices) for _ in range(8))
            try:
                ParticipantAuth.objects.get(auth_code=token)
            except ParticipantAuth.DoesNotExist:
                auth = ParticipantAuth(auth_code=token, participant=participant, auth_code_expire=expire)
                auth.save()
                break
        subject = render_to_string("email/auth_subject.txt")
        subject = "".join(subject.splitlines())
        message = render_to_string("email/auth_body.txt", {"code": auth.auth_code})
        msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [participant.email])
        msg.content_subtype = "html"
        msg.send()
        return HttpResponse(json.dumps({"success": True, "data": {"code": auth.auth_code}}))

class FcmNotification(object):

    ANDROID_API_KEY = settings.ANDROID_API_KEY
    fcm_url = settings.FCM_URL
    headers = {'Content-Type': 'application/json'}
    type = "TEXT"

    def __init__(self):
        self.get_headers()

    def send_message(self, title, body, registration_ids):
        if len(registration_ids) == 0:
            # for demo purpose
            registration_ids = ["dV-BkG4RHNU:APA91bG2QqDRvggfTVrtAAnNpjE4yGXYZIhh5h-VETYI3Q7Pm7mBdSZtnitdJq33tZGStZa_n7T9eTb7vpH71MlYJgbN1Xx_eVuXo_81j57-DkqjwcMPBVAYHNu-Pz0jajHROpmhQ78L"]
        data = dict(data={'title': title, 'type': self.type, 'body': body}, registration_ids=registration_ids)
        r = requests.post(url=self.fcm_url, data=json.dumps(data), headers=self.headers)
        print r.text
        return r.status_code

    def get_headers(self):
        Authorization = 'key=' + self.ANDROID_API_KEY
        self.headers['Authorization'] = Authorization

# class ParticipantVerifyCodeView(View):

#     def get(self, request, error=False, success=False):
#         return render(request, "auth_token_verification.html", context={"error":error, "success": success})

#     def post(self, request):

#         required = ["password", "password2", "code"]

#         for a in required:
#             if a not in request.POST:
#                 return HttpResponse(status=status.HTTP_400_BAD_REQUEST)

#         data = request.POST
#         pass1 = data["password"]
#         pass2 = data["password2"]
#         code = data["code"]
#         errors = []

#         try:
#             auth = ParticipantAuth.objects.get(auth_code=code, auth_code_expire__gte=datetime.now())
#         except ParticipantAuth.DoesNotExist:
#             return self.get(request, "Auth code not found or has expired")

#         if pass1 != pass2:
#             return self.get(request, "Passwords do not match")

#         m = hashlib.sha256()
#         m.update(pass1)

#         auth.password = m.hexdigest()
#         auth.save()

#         return self.get(request, error=False, success=True)

# @method_decorator(csrf_exempt, name='dispatch')
# class ParticipantWebAuthToken(View):

#     def post(self, request):

#         required = ["email", "password", "security_token"]

#         for a in required:
#             if a not in request.POST:
#                 return HttpResponse(status=status.HTTP_400_BAD_REQUEST)

#         email = request.POST["email"]
#         password = request.POST["password"]
#         security_token = request.POST["security_token"]

#         if security_token != "iuh70y78fh2478tg6*T*&F$T(&$^F8gv423958fv70687T)PO&DGCGHI":
#             return HttpResponse(status=status.HTTP_400_BAD_REQUEST)

#         participant = ParticipantApi()._get(email=email)

#         if participant is None:
#             return HttpResponse(json.dumps({"success": False, "error": "Email not found"}))

#         m = hashlib.sha256()
#         m.update(password)

#         try:
#             ParticipantAuth.objects.get(participant=participant, password=m.hexdigest())
#         except ParticipantAuth.DoesNotExist:
#             return HttpResponse(json.dumps({"success": False, "error": "Password incorrect"}))

#         return HttpResponse(json.dumps({"success": True, "error": None, "data": str(participant.uuid)}))
