from django.db.models import Q
from rest_framework import mixins, status, viewsets
from rest_framework.authtoken.models import Token
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from accounts.visibility import visible_companies, visible_seekers

from blog.models import BlogPost
from jobs.models import Job, JobApplication, JobCategory, SavedJob
from jobs.notifications import notify_employer_new_application
from ats.models import ApplicationEvent, ApplicationNote
from ats.services import change_application_status, log_event
from messaging.notifications import notify_new_message
from messaging.services import (
    ConversationClosedError,
    close_conversation,
    get_user_conversations,
    mark_conversation_read,
    reopen_conversation,
    send_message as send_chat_message,
    start_conversation_for_application,
    unread_message_count,
)
from podcast.models import PodcastEpisode
from seekers.models import SeekerProfile
from subscriptions.models import SubscriptionPlan
from testimonials.models import Testimonial

from .permissions import IsCompany, IsMessagingParticipant, IsSeeker
from .serializers import (
    ApplicationNoteCreateSerializer,
    ApplicationNoteSerializer,
    BlogPostSerializer,
    ChatMessageCreateSerializer,
    ChatMessageSerializer,
    CompanySerializer,
    ConversationSerializer,
    EmployerApplicationSerializer,
    JobApplicationSerializer,
    JobCategorySerializer,
    JobDetailSerializer,
    JobListSerializer,
    PodcastEpisodeSerializer,
    SavedJobSerializer,
    SeekerProfileSerializer,
    SubscriptionPlanSerializer,
    TestimonialCreateSerializer,
    TestimonialSerializer,
    TokenObtainSerializer,
    UserSerializer,
)


class JobViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    search_fields = ['title', 'company_name', 'location', 'description']
    ordering_fields = ['created_at', 'salary_min', 'salary_max', 'title']

    def get_queryset(self):
        queryset = Job.objects.filter(is_active=True).select_related('category')
        params = self.request.query_params

        q = params.get('q')
        if q:
            queryset = queryset.filter(
                Q(title__icontains=q)
                | Q(company_name__icontains=q)
                | Q(location__icontains=q)
                | Q(description__icontains=q)
                | Q(category__name__icontains=q)
            )
        location = params.get('location')
        if location:
            queryset = queryset.filter(location__icontains=location)
        job_type = params.get('job_type')
        if job_type:
            queryset = queryset.filter(job_type=job_type)
        experience = params.get('experience_level')
        if experience:
            queryset = queryset.filter(experience_level=experience)
        category = params.get('category')
        if category:
            queryset = queryset.filter(category__slug=category)
        industry = params.get('industry')
        if industry:
            queryset = queryset.filter(company_industry=industry)
        company = params.get('company')
        if company:
            queryset = queryset.filter(company_slug=company)

        return queryset

    def get_serializer_class(self):
        if self.action == 'retrieve':
            return JobDetailSerializer
        return JobListSerializer

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        Job.objects.filter(pk=instance.pk).update(views_count=instance.views_count + 1)
        instance.refresh_from_db()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @action(detail=True, methods=['post'], permission_classes=[IsAuthenticated, IsSeeker])
    def apply(self, request, slug=None):
        job = self.get_object()
        if JobApplication.objects.filter(job=job, applicant=request.user).exists():
            return Response({'detail': 'You have already applied for this job.'}, status=status.HTTP_400_BAD_REQUEST)

        serializer = JobApplicationSerializer(
            data=request.data,
            context={'request': request, 'job': job},
        )
        serializer.is_valid(raise_exception=True)
        application = serializer.save()
        notify_employer_new_application(application)
        return Response(JobApplicationSerializer(application, context={'request': request}).data, status=status.HTTP_201_CREATED)

    @action(detail=True, methods=['post'], permission_classes=[IsAuthenticated])
    def save(self, request, slug=None):
        job = self.get_object()
        saved, created = SavedJob.objects.get_or_create(user=request.user, job=job)
        if not created:
            saved.delete()
            return Response({'saved': False, 'detail': 'Job removed from saved list.'})
        return Response({'saved': True, 'detail': 'Job saved.'}, status=status.HTTP_201_CREATED)


class CompanyViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = CompanySerializer
    search_fields = ['name', 'industry', 'location', 'description']
    ordering_fields = ['name', 'rating', 'created_at']

    def get_queryset(self):
        queryset = visible_companies()
        industry = self.request.query_params.get('industry')
        if industry:
            queryset = queryset.filter(industry_slug=industry)
        tier = self.request.query_params.get('tier')
        if tier:
            queryset = queryset.filter(tier=tier)
        return queryset

    @action(detail=True, methods=['get'])
    def jobs(self, request, slug=None):
        company = self.get_object()
        jobs = Job.objects.filter(is_active=True, company_slug=company.slug).select_related('category')
        page = self.paginate_queryset(jobs)
        serializer = JobListSerializer(page or jobs, many=True, context={'request': request})
        if page is not None:
            return self.get_paginated_response(serializer.data)
        return Response(serializer.data)


class JobCategoryViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = JobCategorySerializer
    pagination_class = None

    def get_queryset(self):
        return JobCategory.active()


class SeekerViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = SeekerProfileSerializer
    search_fields = ['full_name', 'headline', 'skills', 'category', 'location']
    ordering_fields = ['full_name', 'years_experience', 'updated_at']

    def get_queryset(self):
        queryset = visible_seekers()
        category = self.request.query_params.get('category')
        if category:
            queryset = queryset.filter(
                Q(category_slug=category) | Q(job_category__slug=category)
            )
        availability = self.request.query_params.get('availability')
        if availability:
            queryset = queryset.filter(availability=availability)
        return queryset


class BlogPostViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = BlogPostSerializer
    search_fields = ['title', 'excerpt', 'content', 'category']
    ordering_fields = ['published_at', 'title']

    def get_queryset(self):
        queryset = BlogPost.objects.filter(is_published=True)
        category = self.request.query_params.get('category')
        if category:
            queryset = queryset.filter(category__iexact=category)
        return queryset


class PodcastViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = PodcastEpisodeSerializer
    search_fields = ['title', 'description', 'guest_name']
    ordering_fields = ['episode_number', 'published_at']

    def get_queryset(self):
        return PodcastEpisode.objects.filter(is_published=True)


class SubscriptionPlanViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'slug'
    serializer_class = SubscriptionPlanSerializer
    ordering_fields = ['sort_order', 'price_monthly']

    def get_queryset(self):
        queryset = SubscriptionPlan.objects.filter(is_active=True)
        audience = self.request.query_params.get('audience')
        category = self.request.query_params.get('category')
        if audience:
            queryset = queryset.filter(audience=audience)
        if category:
            queryset = queryset.filter(category=category)
        return queryset


class TestimonialViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
    lookup_field = 'pk'
    search_fields = ['full_name', 'role', 'story']
    ordering_fields = ['created_at', 'rating']

    def get_queryset(self):
        return Testimonial.objects.filter(is_approved=True)

    def get_serializer_class(self):
        if self.action == 'create':
            return TestimonialCreateSerializer
        return TestimonialSerializer

    def get_permissions(self):
        if self.action == 'create':
            return [AllowAny()]
        return super().get_permissions()

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(
            {'detail': 'Thank you! Your story was submitted and will appear after review.'},
            status=status.HTTP_201_CREATED,
        )


class ObtainTokenView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = TokenObtainSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, _ = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'user': UserSerializer(user).data,
        })


class MeView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        data = UserSerializer(request.user).data
        if request.user.is_seeker:
            profile = SeekerProfile.objects.filter(user=request.user).first()
            data['seeker_profile'] = SeekerProfileSerializer(profile, context={'request': request}).data if profile else None
            data['applications_count'] = JobApplication.objects.filter(applicant=request.user).count()
            data['saved_jobs_count'] = SavedJob.objects.filter(user=request.user).count()
            data['unread_messages_count'] = unread_message_count(request.user)
        if request.user.is_company:
            company = Company.objects.filter(owner=request.user).first()
            data['company'] = CompanySerializer(company, context={'request': request}).data if company else None
            data['posted_jobs_count'] = Job.objects.filter(posted_by=request.user).count()
            data['applications_received_count'] = JobApplication.objects.filter(
                job__posted_by=request.user
            ).count()
            data['unread_messages_count'] = unread_message_count(request.user)
        return Response(data)


class MyApplicationsView(APIView):
    permission_classes = [IsAuthenticated, IsSeeker]

    def get(self, request):
        applications = JobApplication.objects.filter(applicant=request.user).select_related('job', 'job__category')
        serializer = JobApplicationSerializer(applications, many=True, context={'request': request})
        return Response(serializer.data)


class MySavedJobsView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        saved = SavedJob.objects.filter(user=request.user).select_related('job', 'job__category')
        serializer = SavedJobSerializer(saved, many=True, context={'request': request})
        return Response(serializer.data)


class EmployerApplicationsView(APIView):
    permission_classes = [IsAuthenticated, IsCompany]

    def get(self, request):
        status_filter = request.query_params.get('status')
        job_slug = request.query_params.get('job')
        applications = JobApplication.objects.filter(
            job__posted_by=request.user
        ).select_related('job', 'job__category', 'applicant')
        if status_filter:
            applications = applications.filter(status=status_filter)
        if job_slug:
            applications = applications.filter(job__slug=job_slug)
        serializer = EmployerApplicationSerializer(applications, many=True, context={'request': request})
        return Response(serializer.data)


class ApplicationViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated, IsCompany]
    serializer_class = EmployerApplicationSerializer

    def get_queryset(self):
        queryset = JobApplication.objects.filter(
            job__posted_by=self.request.user,
        ).select_related('job', 'job__category', 'applicant').prefetch_related('notes', 'events', 'interviews')
        status_filter = self.request.query_params.get('status')
        job_slug = self.request.query_params.get('job')
        q = self.request.query_params.get('q')
        if status_filter:
            queryset = queryset.filter(status=status_filter)
        if job_slug:
            queryset = queryset.filter(job__slug=job_slug)
        if q:
            queryset = queryset.filter(
                Q(applicant__first_name__icontains=q)
                | Q(applicant__last_name__icontains=q)
                | Q(applicant__email__icontains=q)
                | Q(job__title__icontains=q)
            )
        return queryset.order_by('-created_at')

    def perform_update(self, serializer):
        instance = self.get_object()
        old_status = instance.status
        application = serializer.save()
        if application.status != old_status:
            change_application_status(
                application,
                application.status,
                self.request.user,
                old_status=old_status,
            )

    @action(detail=True, methods=['post'])
    def notes(self, request, pk=None):
        application = self.get_object()
        note_serializer = ApplicationNoteCreateSerializer(data=request.data)
        note_serializer.is_valid(raise_exception=True)
        note = note_serializer.save(application=application, author=request.user)
        log_event(
            application,
            request.user,
            ApplicationEvent.EventType.NOTE_ADDED,
            message=note.body[:120],
        )
        return Response(
            ApplicationNoteSerializer(note, context={'request': request}).data,
            status=status.HTTP_201_CREATED,
        )

    @action(detail=False, methods=['post'])
    def bulk_status(self, request):
        ids = request.data.get('ids', [])
        new_status = request.data.get('status')
        if not ids or new_status not in dict(JobApplication.Status.choices):
            return Response({'detail': 'Provide ids and a valid status.'}, status=status.HTTP_400_BAD_REQUEST)
        from ats.services import bulk_change_status
        count = bulk_change_status(ids, new_status, request.user, request.user)
        return Response({'updated': count})


class ConversationViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated, IsMessagingParticipant]
    serializer_class = ConversationSerializer

    def get_queryset(self):
        return get_user_conversations(self.request.user)

    def retrieve(self, request, *args, **kwargs):
        conversation = self.get_object()
        mark_conversation_read(conversation, request.user)
        serializer = self.get_serializer(conversation)
        messages = conversation.messages.select_related('sender').all()
        return Response({
            **serializer.data,
            'messages': ChatMessageSerializer(messages, many=True, context={'request': request}).data,
        })

    @action(detail=True, methods=['post'])
    def send(self, request, pk=None):
        conversation = self.get_object()
        if conversation.is_closed:
            return Response({'detail': 'Conversation is closed.'}, status=status.HTTP_403_FORBIDDEN)
        serializer = ChatMessageCreateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            message = send_chat_message(conversation, request.user, serializer.validated_data['body'])
        except ConversationClosedError:
            return Response({'detail': 'Conversation is closed.'}, status=status.HTTP_403_FORBIDDEN)
        notify_new_message(message)
        return Response(
            ChatMessageSerializer(message, context={'request': request}).data,
            status=status.HTTP_201_CREATED,
        )

    @action(detail=True, methods=['post'])
    def close(self, request, pk=None):
        conversation = self.get_object()
        if not request.user.is_company:
            return Response({'detail': 'Only employers can close conversations.'}, status=status.HTTP_403_FORBIDDEN)
        if not close_conversation(conversation, request.user):
            return Response({'detail': 'Not allowed.'}, status=status.HTTP_403_FORBIDDEN)
        return Response(ConversationSerializer(conversation, context={'request': request}).data)

    @action(detail=True, methods=['post'])
    def reopen(self, request, pk=None):
        conversation = self.get_object()
        if not request.user.is_company:
            return Response({'detail': 'Only employers can reopen conversations.'}, status=status.HTTP_403_FORBIDDEN)
        if not reopen_conversation(conversation, request.user):
            return Response({'detail': 'Not allowed.'}, status=status.HTTP_403_FORBIDDEN)
        return Response(ConversationSerializer(conversation, context={'request': request}).data)

    @action(detail=False, methods=['post'])
    def start(self, request):
        application_id = request.data.get('application_id')
        if not application_id:
            return Response({'detail': 'application_id is required.'}, status=status.HTTP_400_BAD_REQUEST)
        conversation = start_conversation_for_application(request.user, application_id)
        if not conversation:
            return Response({'detail': 'Not found.'}, status=status.HTTP_404_NOT_FOUND)
        return Response(
            ConversationSerializer(conversation, context={'request': request}).data,
            status=status.HTTP_201_CREATED,
        )
