from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.views import APIView

from .algorithms import ProcessingError
from .registry import operation_metadata
from .selectors import image_session_get, image_state_get, image_states_list, processing_job_get
from .services import (
    batch_job_create,
    combine_states,
    image_session_create,
    image_session_process,
    image_state_apply_operation,
    image_state_delete,
    image_state_payload,
    image_states_payload,
    processing_job_payload,
)


def error_response(message, code=status.HTTP_400_BAD_REQUEST):
    return Response({"detail": message}, status=code)


class ImageUploadView(APIView):
    class InputSerializer(serializers.Serializer):
        image = serializers.ImageField(required=False)
        image_base64 = serializers.CharField(required=False, allow_blank=False)

        def validate(self, attrs):
            if not attrs.get("image") and not attrs.get("image_base64"):
                raise serializers.ValidationError("Provide image or image_base64.")
            return attrs

    def post(self, request):
        serializer = self.InputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            payload = image_session_create(
                uploaded_file=serializer.validated_data.get("image"),
                image_base64=serializer.validated_data.get("image_base64"),
            )
            return Response(payload, status=status.HTTP_201_CREATED)
        except ProcessingError as exc:
            return error_response(str(exc))


class ProcessView(APIView):
    class InputSerializer(serializers.Serializer):
        session_id = serializers.UUIDField()
        operation = serializers.CharField()
        params = serializers.DictField(required=False, default=dict)

    def post(self, request):
        serializer = self.InputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        session = image_session_get(session_id=serializer.validated_data["session_id"])
        if session is None:
            return error_response("Image session does not exist.", status.HTTP_404_NOT_FOUND)

        try:
            payload = image_session_process(
                session=session,
                operation=serializer.validated_data["operation"],
                params=serializer.validated_data.get("params", {}),
            )
            return Response(payload)
        except ProcessingError as exc:
            code = status.HTTP_410_GONE if str(exc) == "Image session has expired." else status.HTTP_400_BAD_REQUEST
            return error_response(str(exc), code)


class OperationsView(APIView):
    def get(self, request):
        return Response({"operations": operation_metadata()})


class SessionStatesView(APIView):
    def get(self, request, session_id):
        session = image_session_get(session_id=session_id)
        if session is None:
            return error_response("Image session does not exist.", status.HTTP_404_NOT_FOUND)
        return Response({"session_id": str(session.id), "states": image_states_payload(states=image_states_list(session_id=session.id))})


class StateOperationView(APIView):
    class InputSerializer(serializers.Serializer):
        operation = serializers.CharField()
        params = serializers.DictField(required=False, default=dict)

    def post(self, request, state_id):
        serializer = self.InputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        state = image_state_get(state_id=state_id)
        if state is None:
            return error_response("Image state does not exist.", status.HTTP_404_NOT_FOUND)
        try:
            payload = image_state_apply_operation(
                state=state,
                operation=serializer.validated_data["operation"],
                params=serializer.validated_data.get("params", {}),
            )
            return Response(payload, status=status.HTTP_201_CREATED)
        except ProcessingError as exc:
            code = status.HTTP_410_GONE if str(exc) == "Image session has expired." else status.HTTP_400_BAD_REQUEST
            return error_response(str(exc), code)


class StateDetailView(APIView):
    def delete(self, request, state_id):
        state = image_state_get(state_id=state_id)
        if state is None:
            return error_response("Image state does not exist.", status.HTTP_404_NOT_FOUND)
        try:
            image_state_delete(state=state)
            return Response(status=status.HTTP_204_NO_CONTENT)
        except ProcessingError as exc:
            return error_response(str(exc))


class StateCombineView(APIView):
    class InputSerializer(serializers.Serializer):
        operation = serializers.ChoiceField(choices=["add", "subtract", "dot_product", "average", "and", "or"])
        state_ids = serializers.ListField(child=serializers.UUIDField(), min_length=2)
        params = serializers.DictField(required=False, default=dict)

    def post(self, request):
        serializer = self.InputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        states = [image_state_get(state_id=state_id) for state_id in serializer.validated_data["state_ids"]]
        if any(state is None for state in states):
            return error_response("One or more image states do not exist.", status.HTTP_404_NOT_FOUND)
        try:
            payload = combine_states(
                states=states,
                operation=serializer.validated_data["operation"],
                params=serializer.validated_data.get("params", {}),
            )
            return Response(payload, status=status.HTTP_201_CREATED)
        except ProcessingError as exc:
            return error_response(str(exc))


class StateHistogramView(APIView):
    def get(self, request, state_id):
        state = image_state_get(state_id=state_id)
        if state is None:
            return error_response("Image state does not exist.", status.HTTP_404_NOT_FOUND)
        return Response({"state_id": str(state.id), "histogram": state.histogram, "state": image_state_payload(state=state, include_image=False)})


class BatchView(APIView):
    class InputSerializer(serializers.Serializer):
        operation = serializers.ChoiceField(choices=["average", "subtract"])
        session_ids = serializers.ListField(child=serializers.UUIDField(), min_length=2)
        params = serializers.DictField(required=False, default=dict)

    def post(self, request):
        serializer = self.InputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        job = batch_job_create(
            operation=serializer.validated_data["operation"],
            session_ids=serializer.validated_data["session_ids"],
            params=serializer.validated_data.get("params", {}),
        )
        return Response({"job_id": str(job.id), "status": job.status}, status=status.HTTP_202_ACCEPTED)


class JobDetailView(APIView):
    def get(self, request, job_id):
        job = processing_job_get(job_id=job_id)
        if job is None:
            return error_response("Job does not exist.", status.HTTP_404_NOT_FOUND)
        return Response(processing_job_payload(job=job))


class HealthView(APIView):
    def get(self, request):
        return Response({"status": "ok"})
