# Copyright 2021-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: workers."""

import logging

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

from debusine.db.models import (
    Token,
    WorkRequest,
    Worker,
)
from debusine.server.serializers import (
    WorkRequestCompletedSerializer,
    WorkRequestSerializer,
    WorkerRegisterSerializer,
)
from debusine.server.views.base import (
    IsWorkerAuthenticated,
    ProblemResponse,
)

logger = logging.getLogger(__name__)


class RegisterView(views.APIView):
    """View used by workers to register to debusine."""

    def post(self, request):
        """Worker registers (sends token and fqdn)."""
        worker_register = WorkerRegisterSerializer(data=request.data)

        if not worker_register.is_valid():
            return ProblemResponse(
                "Cannot deserialize worker",
                validation_errors=worker_register.errors,
            )

        token_key = worker_register.validated_data['token']
        fqdn = worker_register.validated_data['fqdn']
        worker_type = worker_register.validated_data['worker_type']

        token_hash = Token._generate_hash(token_key)
        token, _ = Token.objects.get_or_create(hash=token_hash)

        Worker.objects.create_with_fqdn(fqdn, token, worker_type=worker_type)

        logger.info('Client registered. Token key: %s', token_key)

        return Response(status=status.HTTP_201_CREATED)


class GetNextWorkRequestView(views.APIView):
    """View used by workers to request a task."""

    permission_classes = [IsWorkerAuthenticated]

    def get(self, request):
        """Return the task to build."""
        token_key = request.headers['token']
        worker = Worker.objects.get_worker_by_token_key_or_none(token_key)

        work_request = WorkRequest.objects.running(worker=worker).first()

        if work_request is None:
            work_request = WorkRequest.objects.pending(worker=worker).first()

        if work_request:
            content = WorkRequestSerializer(work_request).data
            work_request.mark_running()
            status_code = status.HTTP_200_OK
        else:
            # There is no work request available for the worker
            content = None
            status_code = status.HTTP_204_NO_CONTENT

        return Response(content, status=status_code)


class UpdateWorkRequestAsCompletedView(views.APIView):
    """View used by the workers to mark a task as completed."""

    permission_classes = [IsWorkerAuthenticated]

    def put(self, request, work_request_id: int):
        """Mark a work request as completed."""
        token_key = request.headers['token']
        worker = Worker.objects.get_worker_by_token_key_or_none(token_key)

        try:
            work_request = WorkRequest.objects.get(pk=work_request_id)
        except WorkRequest.DoesNotExist:
            return ProblemResponse(
                "Work request not found",
                status_code=status.HTTP_404_NOT_FOUND,
            )

        if work_request.worker == worker:
            work_request_completed_serializer = WorkRequestCompletedSerializer(
                data=request.data
            )

            if work_request_completed_serializer.is_valid():
                work_request.mark_completed(
                    work_request_completed_serializer.validated_data['result']
                )
                content = None
                status_code = status.HTTP_200_OK
            else:
                return ProblemResponse(
                    "Cannot change work request as completed",
                    validation_errors=work_request_completed_serializer.errors,
                )
        else:
            return ProblemResponse(
                "Invalid worker to update the work request",
                status_code=status.HTTP_401_UNAUTHORIZED,
            )

        return Response(content, status=status_code)


class UpdateWorkerDynamicMetadataView(views.APIView):
    """View used by the workers to post dynamic metadata."""

    permission_classes = [IsWorkerAuthenticated]

    def put(self, request):
        """Update Worker dynamic metadata."""
        token_key = request.headers['token']
        worker = Worker.objects.get_worker_by_token_key_or_none(token_key)

        worker.set_dynamic_metadata(request.data)

        return Response(status=status.HTTP_204_NO_CONTENT)
