# Copyright 2024 The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Views for the server application: lookups."""

import logging


from rest_framework import status, views
from rest_framework.parsers import JSONParser
from rest_framework.response import Response

from debusine.db.models import Worker
from debusine.server.collections.lookup import lookup_multiple, lookup_single
from debusine.server.serializers import (
    LookupMultipleSerializer,
    LookupResponseSerializer,
    LookupSingleSerializer,
)
from debusine.server.views.base import (
    IsWorkerAuthenticated,
    ProblemResponse,
)
from debusine.tasks.models import LookupMultiple


logger = logging.getLogger(__name__)


class LookupSingleView(views.APIView):
    """View used to look up a single collection item."""

    permission_classes = [IsWorkerAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        """
        Resolve a lookup for a single collection item.

        The request data must be a :ref:`lookup-single`.
        """
        token_key = request.headers["token"]
        worker = Worker.objects.get_worker_by_token_key_or_none(token_key)

        lookup_deserialized = LookupSingleSerializer(data=request.data)
        lookup_deserialized.is_valid(raise_exception=True)
        work_request = lookup_deserialized.validated_data["work_request"]
        lookup_user = request.user if request.user.is_authenticated else None

        if work_request.worker != worker:
            return ProblemResponse(
                f"Work request {work_request.id} is not assigned to the "
                f"authenticated worker",
                status_code=status.HTTP_401_UNAUTHORIZED,
            )

        try:
            result = lookup_single(
                lookup_deserialized.validated_data["lookup"],
                work_request.workspace,
                user=lookup_user,
                default_category=lookup_deserialized.validated_data.get(
                    "default_category"
                ),
                workflow_root=work_request.get_workflow_root(),
                expect_type=lookup_deserialized.validated_data["expect_type"],
            )
        except KeyError as e:
            return ProblemResponse(
                "No matches",
                detail=str(e),
                status_code=status.HTTP_404_NOT_FOUND,
            )
        except LookupError as e:
            return ProblemResponse(
                "Lookup error",
                detail=str(e),
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        return Response(
            LookupResponseSerializer(result).data, status=status.HTTP_200_OK
        )


class LookupMultipleView(views.APIView):
    """View used to look up multiple collection items."""

    permission_classes = [IsWorkerAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        """
        Resolve a lookup for a single collection item.

        The request data must be a :ref:`lookup-multiple`.
        """
        token_key = request.headers["token"]
        worker = Worker.objects.get_worker_by_token_key_or_none(token_key)

        lookup_deserialized = LookupMultipleSerializer(data=request.data)
        lookup_deserialized.is_valid(raise_exception=True)
        work_request = lookup_deserialized.validated_data["work_request"]
        lookup_user = request.user if request.user.is_authenticated else None

        if work_request.worker != worker:
            return ProblemResponse(
                f"Work request {work_request.id} is not assigned to the "
                f"authenticated worker",
                status_code=status.HTTP_401_UNAUTHORIZED,
            )

        try:
            lookup = LookupMultiple.parse_obj(
                lookup_deserialized.validated_data["lookup"]
            )
        except ValueError as e:
            return ProblemResponse(
                "Cannot deserialize lookup",
                validation_errors={"lookup": str(e)},
            )

        try:
            result = lookup_multiple(
                lookup,
                work_request.workspace,
                user=lookup_user,
                default_category=lookup_deserialized.validated_data.get(
                    "default_category"
                ),
                workflow_root=work_request.get_workflow_root(),
                expect_type=lookup_deserialized.validated_data["expect_type"],
            )
        except LookupError as e:
            return ProblemResponse(
                "Lookup error",
                detail=str(e),
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        return Response(
            LookupResponseSerializer(result, many=True).data,
            status=status.HTTP_200_OK,
        )
