import base64
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from web3.exceptions import CannotHandleRequest
from raven.contrib.django.raven_compat.models import client

from app.managers import TimestampManager
from app.utils import Utils

class Stamp(APIView):
    """
    [POST]
    Permite realizar un stamp de un archivo

    Parámetros recibidos:
    [Content-Type:application/json]
    - file_hash: El hash del archivo encodeado en sha256


    Devuelve un proof para poder verificar en el futuro que el archivo fue incluido a la Blockchain

    Ejemplo:
    {
      "file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547"
    }
    """

    def post(self, request):

        try:
            if not request.data.get('file_hash'):
                raise ValidationError('file_hash')

            file_hash = request.data.get('file_hash')

            proof_hash = Utils.get_proof_hash(file_hash)

            tx_hash = TimestampManager.stamp(proof_hash, file_hash)

            proof = proof_hash + '-' + tx_hash.hex()

            return Response({_('status'): _('success'), _('proof'): base64.b64encode(proof.encode('utf-8')).decode('utf-8')}, status=status.HTTP_200_OK)

        except ValidationError as e:
            return Response({_('status'): _('failure'), _('messages'): _('parameter_missing') % e.message}, status=status.HTTP_400_BAD_REQUEST)
        except CannotHandleRequest:
            return Response({_('status'): _('failure'), _('messages'): _('could_not_connect')}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
        except Exception:
            client.captureException()
            return Response({_('status'): _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class Verify(APIView):
    """
    [POST]
    Permite verificar que un archivo fue subido a la Blockchain

    Parámetros recibidos:
    [Content-Type:application/json]
    - file_hash: El hash del archivo original encodeado en sha256
    - proof: Proof recibido como prueba al momento de realizar el stamp

    Devuelve número de bloque, fecha y hora de subida a la Blockchain

    Ejemplo:
    {
      "file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547",
      "proof": "NzNkYzA5OGJkODlmZjdlMjc4OGFjMzJlNmU2ODdiOTdmODdiMTBjMWIyNzg5OTFlMDNkN2E2YWVkMDk3ODJkZTAxLTB4NGM2ZmNiNDBhMmUyZGVjYzc2YWQzMjM3MDU2NzZjMjljYWE1MmIyYjZkMDdiMDIzYjBhY2EzOWYzZGIxYmRlZg=="
    }
    """
    def post(self, request):

        try:

            if not request.data.get('file_hash'):
                raise ValidationError('file_hash')

            if not request.data.get('proof'):
                raise ValidationError('proof')

            file_hash = request.data.get('file_hash')
            base64_proof = request.data.get('proof')

            proof = base64.b64decode(base64_proof).decode('utf-8')
            proof_hash, tx_hash = proof.split('-')
            contract_version = proof_hash[-2:]

            verified = TimestampManager.verify(contract_version, proof_hash, file_hash)

            if verified:
                block_number = TimestampManager.get_block_number(contract_version, proof_hash)
                block = TimestampManager.get_block(block_number)

                return Response({_('status'): _('success'),_('messages'): _('file_uploaded') % (file_hash, str(block.number), str(Utils.datetime_from_timestamp(block.timestamp)))},status=status.HTTP_200_OK)
            else:
                try:
                    transaction = TimestampManager.get_transaction(tx_hash)
                    if transaction and not transaction.blockNumber:
                        return Response({_('status'): _('pending'), _('messages'): _('transaction_pending')}, status=status.HTTP_200_OK)
                except ValueError:
                    pass

                return Response({_('status'): _('failure'), _('messages'): _('file_not_found')},status=status.HTTP_404_NOT_FOUND)

        except ValidationError as e:
            return Response({_('status'): _('failure'), _('messages'): _('parameter_missing') % e.message}, status=status.HTTP_400_BAD_REQUEST)
        except CannotHandleRequest:
            return Response({_('status'): _('failure'), _('messages'): _('could_not_connect')}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
        except Exception:
            client.captureException()
            return Response({_('status'): _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)