import logging
from rest_framework import viewsets, status
from rest_framework.exceptions import ValidationError
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from django_filters.rest_framework import DjangoFilterBackend

from apps.calls.constants import BotType
from apps.appointments.filters import AppointmentFilter
from apps.appointments.services import AppointmentService, WebhookEventService
from apps.appointments.pagination import AppointmentLimitOffsetPagination
from apps.appointments.repositories import AppointmentRepository
from apps.appointments.serializers import (
    AppointmentSerializer,
    BookAppointmentInputSerializer,
    WebhookSuccessSerializer,
)


logger = logging.getLogger(__name__)


class AppointmentViewSet(viewsets.ModelViewSet):
    queryset = AppointmentRepository.get_queryset()
    serializer_class = AppointmentSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = AppointmentLimitOffsetPagination
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = AppointmentFilter
    search_fields = ["name", "twilio_call_sid", "call__from_number"]
    ordering_fields = [
        "scheduled_date",
        "call__from_number",
        "created_at",
        "name"
    ]
    ordering = ["-created_at"]

    def get_queryset(self):
        user = self.request.user

        if user.is_superuser:
            return self.queryset.order_by("-scheduled_date")

        if getattr(user, "active_company", None):
            try:
                return (
                    self.queryset
                    .filter(
                        company=user.active_company,
                        bot_type=BotType.SALES_BOT.value
                    )
                    .order_by("-scheduled_date")
                )
            except Exception as exc:
                logger.info(exc)
                return self.queryset.none()

        return self.queryset.none()

    ## ========================= WEB HOOKS ===========================
    @action(
        detail=False,
        methods=["post"],
        url_path="book-appointment",
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def book_appointment(self, request):
        """
        Endpoint for sales bot webhook.
        """
        event = WebhookEventService.log_event(
            request,
            "retell",
            'Book Sales Appointment'
        )

        serializer = BookAppointmentInputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            AppointmentService.book_sales_callback(serializer.validated_data)
            if event:
                event.mark_success()
        except Exception as exc:
            logger.exception("Failed booking sales appointment: %s", exc)
            if event:
                event.mark_failed(exc)
            return Response(
                {"message": "Failed to book appointment"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        return Response(
            WebhookSuccessSerializer(
                {"message": "Appointment booked successfully"}
            ).data, status=status.HTTP_200_OK
        )

    @action(
        detail=False,
        methods=["post"],
        url_path="book-service-appointment",
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def book_service_appointment(self, request):
        """
        Endpoint for service bot webhook.
        """

        event = WebhookEventService.log_event(
            request,
            "retell",
            'Book Service Appointment'
        )

        serializer = BookAppointmentInputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        logger.info("Payload for appointment booking:")
        logger.info(request.data)

        try:
            AppointmentService.book_service_appointment(serializer.validated_data)
            if event:
                event.mark_success()

        except ValidationError as exc:
            logger.warning("Validation error booking appointment: %s", exc)
            if event:
                event.mark_failed(exc)
            return Response(
                {"detail": exc.detail},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception as exc:
            logger.exception(
                "Failed booking service appointment: %s",
                exc
            )
            if event:
                event.mark_failed(exc)
            return Response(
                {"message": "Failed to book appointment"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        return Response(
            WebhookSuccessSerializer(
                {"message": "Appointment booked successfully"}
            ).data, status=status.HTTP_200_OK
        )

    @action(
        detail=False,
        methods=["post"],
        url_path="week-day",
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def week_day(self, request):
        """
        Endpoint for week day webhook.
        """
        event = WebhookEventService.log_event(
            request,
            "retell",
            'Get Week Day'
        )

        try:
            if not event:
                return Response(
                    {"message": "Unable to process request as empty event"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            booking_datetime = event.raw_payload.get('args', {}).get('booking_datetime', None)
            week_day = AppointmentService.get_week_day(booking_datetime)
            event.mark_success()
        except Exception as exc:
            logger.exception("Failed to get week day: %s", exc)
            if event:
                event.mark_failed(exc)
            return Response(
                {"message": f"Failed to get week day {exc}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        return Response(
            {"week_day": week_day},
            status=status.HTTP_200_OK
        )
