# -*- coding: utf-8 -*-
from django.shortcuts import render, render_to_response, redirect
from django.db.models import Q
from django.conf import settings
from django.core.mail import EmailMessage
from django.contrib import auth

from malwaredb.tasks import profiling
from malwaredb.models import Malware, Upload, VirusTotalInfo, Hooklog
from malwaredb.forms import MalwareUploadFileForm, ContactForm, UserCreationForm
from user_account.models import UserAccount

from django.contrib.auth.models import User
from django.contrib.auth.forms import AuthenticationForm
from django.test import RequestFactory

from datetime import datetime

from rest_framework.views import APIView
from rest_framework_expiring_authtoken.models import ExpiringToken
from rest_framework_expiring_authtoken.views import ObtainExpiringAuthToken
from rest_framework.decorators import api_view, permission_classes, parser_classes
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.parsers import MultiPartParser, FormParser
from malwaredb.serializer import UploadFileDataSerializer

import hashlib
import tempfile
import base64

MEDIA_ROOT = settings.MEDIA_ROOT
TEMPLATE_ROOT = settings.TEMPLATES[0]['DIRS'][0]

# ====================================  API ====================================

class CustomObtainExpiringAuthToken(ObtainExpiringAuthToken):

    def post(self, request, *args, **kwargs):
        submit_data = dict(request.data)
        try:
            user = User.objects.get(username=submit_data['username'][0])
        except KeyError:
            return Response({'status_code': 400, "detail": "No input username"})
        except User.DoesNotExist:
            return Response({'status_code': 404, "detail": "User not found"})

        try:
            authenticate = user.check_password(submit_data['password'][0])
        except KeyError:
            return Response({'status_code': 400, "detail": "No input password"})

        if authenticate:
            token, _ = ExpiringToken.objects.get_or_create(user=user)

            if token.expired():
                # If the token is expired, generate a new one.
                token.delete()
                token = ExpiringToken.objects.create(
                    user=user
                )

            return Response({'status_code': 200, 'token': token.key})
        else:
            return Response({'status_code': 401, "detail": "Wrong Password"})


class SubmitFileView(APIView):

    def post(self, request, *args, **kwargs):
        submit_data = dict(request.data)

        try:
            # transfer base64 to file
            f = submit_data["file"][0]
            with tempfile.NamedTemporaryFile(dir=MEDIA_ROOT + "/tmp") as temp:
                temp.write(base64.decodestring(f))

                temp.seek(0)
                factory = RequestFactory()
                fake_request = factory.post('/malwareupload/', {'uploadedfile': open(temp.name, 'rb'),
                                                                'streaming': submit_data.get("streaming", False),
                                                                })
                fake_request.user = request.user
                hash_value = malwareuploadFromLocal(fake_request)
            if submit_data.get("streaming", False):
                return Response({'status_code': 200, 'SHA-256': hash_value,
                                 'server_ip': "140.112.107.39", 'server_port': '10001'})
            return Response({'status_code': 200, 'SHA-256': hash_value})
        except KeyError:
            return Response({'status_code': 400, "detail": "No submit file"})


@api_view(http_method_names=['POST'])
@permission_classes((IsAuthenticated,))
@parser_classes((MultiPartParser, FormParser))
def api_upload_file(request):
    validator = UploadFileDataSerializer(data=request.data)
    if not validator.is_valid():
        return Response(validator.errors)

    upload_file = validator.validated_data['file']

    sha256_hash = get_upload_file_sha256_hash(upload_file)
    handling_file(upload_file, sha256_hash, request)

    return Response({'status_code': 200, 'SHA-256': sha256_hash})


class GetHooklogView(APIView):

    def post(self, request, *args, **kwargs):
        # get data
        submit_data = dict(request.data)
        sha256 = submit_data["SHA256"][0]

        # check malware exist
        try:
            mal = Malware.objects.get(hash=sha256)
        except Malware.DoesNotExist:
            return Response({'status_code': 404, 'detail': 'SHA-256 not found'})

        # check profiling fininshed
        if not mal.create_hooklog_state:
            return Response({'status_code': 404, 'detail': 'Profiling result not found'})

        # success, return hooklog
        hooklogs = Hooklog.objects.filter(malware=mal)
        response_hooklog = []
        for hooklog in hooklogs:
            hooklog_dict = {
                'filename': hooklog.hooklog_file.name.split('/')[1],
                'hooklog': base64.b64encode(hooklog.hooklog)
            }
            response_hooklog.append(hooklog_dict)

        return Response({'status_code': 200, 'hooklog': response_hooklog})

# ====================================  /API ====================================


def index(request):
    upload_form = MalwareUploadFileForm()
    signup_form = UserCreationForm()
    login_form = AuthenticationForm()

    if request.method == 'POST':

        if 'log-in' in request.POST:
            login_form = AuthenticationForm(data=request.POST)
            if login_form.is_valid():
                user = auth.authenticate(username=login_form.cleaned_data['username'],
                                         password=login_form.cleaned_data['password'])

                if user is not None and user.is_active:
                    auth.login(request, user)
                login_success = True
            else:
                login_fail = True

        elif 'sign-up' in request.POST:
            signup_form = UserCreationForm(request.POST)
            if signup_form.is_valid():
                user = signup_form.save()
                user_account = UserAccount.objects.create(user=user, create_date=datetime.now())

                user = auth.authenticate(username=signup_form.cleaned_data['username'],
                                         password=signup_form.cleaned_data['password1'])
                if user is not None and user.is_active:
                    auth.login(request, user)

                signup_success = True
            else:
                signup_fail = True

        elif 'upload' in request.POST:
            upload_form = MalwareUploadFileForm(request.POST, request.FILES)
            if upload_form.is_valid():
                f = request.FILES['file']

                sha256_hash = get_upload_file_sha256_hash(f)
                upload_success = handling_file(f, sha256_hash, request)

    malware_num = len(Malware.objects.filter(~Q(detectionrate=0)))
    upload_num = len(Upload.objects.all())
    analysis_num = len(Malware.objects.all())

    return render(request, 'malware.html', locals())


def malware_search(request):
    vendor_list = None

    # Search by Hash
    if request.GET.get('MD5', ''):
        searchText = request.GET['MD5']
        malware = Malware.objects.filter(Q(md5=searchText)).first()
        vendor_list = VirusTotalInfo.objects.filter(Q(malware=malware)).order_by('vendor')
    elif request.GET.get('SHA1', ''):
        searchText = request.GET['SHA1']
        malware = Malware.objects.filter(Q(sha1=searchText)).first()
        vendor_list = VirusTotalInfo.objects.filter(Q(malware=malware)).order_by('vendor')
    elif request.GET.get('SHA256', ''):
        searchText = request.GET['SHA256']
        malware = Malware.objects.filter(Q(sha256=searchText)).first()
        vendor_list = VirusTotalInfo.objects.filter(Q(malware=malware)).order_by('vendor')

    # Search by Keyword
    elif request.GET.get('keyword', ''):
        keyword = request.GET['keyword']
        vendor_list = VirusTotalInfo.objects.filter(Q(label__contains=keyword)).order_by('vendor')

    # Search by Family
    elif request.GET.get('family', ''):
        return render_to_response('malware_family.html')

    # Search by Behavior Group
    elif request.GET.get('behaviorgroup', ''):
        return render_to_response('malware_behavior.html')

    return render_to_response('malware_search.html', {'vendor_list': vendor_list})


def malware_family(request):
    return render_to_response('malware_family.html')


def malware_behavior(request):
    return render_to_response('malware_behavior.html')


def malware_download(request, hash_value, page):
    malware_hooklog = Hooklog.objects.filter(malware__hash=hash_value)[int(page) - 1]

    from django.http import HttpResponse
    response = HttpResponse(malware_hooklog.hooklog_file, content_type='text/plain')
    response['Content-Disposition'] = 'attachment; filename={}'.format(hash_value + "_" + page)
    return response


def malware_detail(request):

    malware_name = request.GET['malware']

    if Malware.objects.filter(hash=malware_name):
        malware = Malware.objects.filter(hash=malware_name).first()
        hooklog_list = Hooklog.objects.filter(malware=malware)
        return render_to_response('malware_detail.html', {'malware': malware,
                                                          'hooklog_list': hooklog_list,
                                                          'page': 1})

    return render(request, '404.html')


def load_hooklog_page(request, hash_value, page):

    if page and int(page) > 0:

        hooklog = Hooklog.objects.filter(malware__hash=hash_value)[int(page)-1]
        content = hooklog.hooklog.split('#')

        title = '{hash}_{pid} Bahavior Analysis'.format(hash=hash_value, pid=content[0].split()[0])

        invoke_api_list = []
        for line in content[1:]:
            data = line.strip().split('\n')
            invoke_api = {'timestamp': data[0].strip(), 'api_name': data[1].strip(), }

            args_list = []
            for arg in data[2:]:
                args_list.append(arg.strip())

            if args_list:
                invoke_api['args'] = args_list

            invoke_api_list.append(invoke_api)

        return render(request, "hooklog.html", {'title': title, 'invoke_api_list': invoke_api_list,})

    return render(request, '404.html')


def contact(request):
    form_class = ContactForm

    if request.method == 'POST':
        form = form_class(data=request.POST)

        if form.is_valid():
            contact_name = request.POST.get('name', '')
            contact_email = request.POST.get('email', '')
            form_title = request.POST.get('title', '')
            form_content = request.POST.get('content', '')

            content = "Name: {0}\n" \
                      "Email: {1}\n" \
                      "Ttile: {2}\n" \
                      "Content: {3}".format(contact_name, contact_email, form_title, form_content)

            email = EmailMessage(
                "New contact form submission",
                content,
                "Your website" + '',
                ['yunyun1113c@gmail.com'],
                headers={'Reply-To': contact_email}
            )
            email.send()
            return redirect('contact')
    return render(request, 'contact.html', {
        'form': form_class,
    })


def malwareuploadFromLocal(request):
    if request.method == 'POST':
        f = request.FILES['uploadedfile']

        hash_value = get_upload_file_sha256_hash(f)
        handling_file(f, hash_value, request)

    return hash_value


def handling_file(f, hash_value, request):

    # Add Upload Info
    u = Upload(file=f, name=f.name, size=f.size, hash=hash_value,
               submissiondate=datetime.now(), user_ip=request.META.get('REMOTE_ADDR'))

    if request.user:
        try:
            user_account = UserAccount.objects.get(user=request.user)
            u.user_account = user_account
        except TypeError:
            guest_user, _ = User.objects.get_or_create(username='guest')
            guest_user_account, _ = UserAccount.objects.get_or_create(
                user=guest_user, defaults={'create_date': datetime.now()})
            u.user_account = guest_user_account
    u.save()

    # Add Malware Info
    if Malware.objects.filter(hash=hash_value):
        return False

    m = Malware(hash=hash_value, submissiondate=datetime.now())
    m.save()

    # Celery task
    streaming = request.POST.get("streaming", False)
    profiling.delay(hash_value, streaming)

    return True


def get_upload_file_sha256_hash(f):
    hash_value = hashlib.sha256()
    for chunk in f.chunks():
        hash_value.update(chunk)
    return hash_value.hexdigest()


# HTTP Error 404
def bad_request(request):
    return render(request, '404.html')


# HTTP Error 500
def server_error(request):
    return render(request, '500.html')
