"""
Views for the call APIs.
"""

import json
import logging
import mimetypes
import os
import requests
from datetime import datetime, timedelta
from django.conf import settings
from django.db.models import Count, Sum, Max, Avg, Q
from django.db.models.functions import TruncDate, ExtractHour, TruncHour, Round
from dateutil.relativedelta import relativedelta
from django.http import FileResponse, Http404
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import permissions
from rest_framework import viewsets, status
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import action
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from django.db.models.functions import Extract
from apps.companies.models import Company, CompanyBotSettings
from apps.core.models import CompanyInfo, User
from apps.calls.utils import get_bot_name, send_booking_email, send_booking_sms
from apps.appointments.models import Appointment
from apps.calls.pagination import CallBDCLimitOffsetPagination
from apps.customers.models import Customer
from apps.companies.utils import get_company_working_info, check_sales_time_by_phone
from apps.calls.constants import TRANSFER_STATUS, SENTIMENT, BotType
from apps.calls.filters import CallFilter
from apps.calls.models import Call, CallActivity, UserMessage, Notification, Notification
from apps.calls.pagination import CallLimitOffsetPagination
from apps.calls.serializers import (
    CallDetailSerializer,
    TopRepeatedCallersSerializer,
    DateRangeSerializer,
    CallActivitySerializer
)
from django.db.models import (
    OuterRef, Subquery, Count, FloatField, F, IntegerField, Value, Case, When
)
from django.db.models.functions import Coalesce
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status

from apps.userprofile.models import UserProfile
from apps.calls.serializers import ReadCallSerializer
from apps.calls.services import (
    RecordingService,
    StorageService,
    get_caller_name_by_phone_number,
    get_twilio_client,
    NotificationService
)
import pytz
from django.utils import timezone
from apps.calls.tasks import process_booking_intent

logger = logging.getLogger(__name__)


class CallViewSet(viewsets.ModelViewSet):
    """View for manage call APIs."""
    serializer_class = CallDetailSerializer
    queryset = Call.objects.all()
    permission_classes = [IsAuthenticated]
    pagination_class = CallLimitOffsetPagination
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = CallFilter
    search_fields = ['from_number', 'to_number', 'transcript', 'summary', 'caller_name',]
    ordering_fields = ['created_at', 'duration', 'cost']
    ordering = ['-created_at']

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

        if user.is_superuser:
            qs = self.queryset
        elif user.active_company:
            try:
                # company_info = Company.objects.get(phone=user.active_company.phone)
                qs = self.queryset.filter(company=user.active_company)
                notification_calls = Call.objects.filter(
                    notifications__recipient=user
                )
                qs = (qs | notification_calls).distinct()
            except Company.DoesNotExist:
                return self.queryset.none()
        else:
            return self.queryset.none()

        if self.action == "list":
            qs = qs.filter(bot_type=BotType.SERVICE_BOT.value)

        return qs.order_by('-id')

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

    #     if user.is_superuser:
    #         return self.queryset.filter(bot_type=BotType.SERVICE_BOT.value).order_by('-id')

    #     if user.active_company:
    #         try:
    #             company_info = Company.objects.get(phone=user.active_company.phone)
    #             return self.queryset.filter(company=company_info, bot_type=BotType.SERVICE_BOT.value).order_by('-id')
    #         except Company.DoesNotExist:
    #             return self.queryset.none()

    #     return self.queryset.none()

    def get_serializer_class(self):
        """Return the serializer class for request."""
        if self.action == 'list':
            return CallDetailSerializer

        return self.serializer_class

    def perform_create(self, serializer):
        """Create a new call."""
        serializer.save()

    @action(detail=False, methods=['get'], url_path='summary')
    def summary(self, request):
        """Get call summary statistics using DRF filters."""
        filtered_queryset = self.filter_queryset(self.get_queryset())

        summary = filtered_queryset.aggregate(
            total_calls=Count('id'),
            total_missed_calls=Count('id', filter=Q(transfer_status=TRANSFER_STATUS.FAILED.value)),
            total_attended_calls=Count('id', filter=Q(transfer_status=TRANSFER_STATUS.SUCCESSFUL.value)),
            average_call_time=Avg('duration'),
            unique_callers=Count('from_number', distinct=True),
            repeated_callers=Count('from_number') - Count('from_number', distinct=True),
        )

        report_data = {
            'total_calls': summary['total_calls'] or 0,
            'total_missed_calls': summary['total_missed_calls'] or 0,
            'total_attended_calls': summary['total_attended_calls'] or 0,
            'average_call_time_seconds': summary['average_call_time'] or 0,
            'unique_callers': summary['unique_callers'] or 0,
            'repeated_callers': summary['repeated_callers'] or 0,
        }

        return Response(report_data, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='bdc-calls-old')
    def get_bdc_calls(self, request):
        """Get calls for a specific BDC number from company table."""
        user = request.user

        bdc_number = None
        if user.is_superuser:
            bdc_number = request.query_params.get('bdc_number')
            if not bdc_number:
                return Response({
                    'detail': 'Please provide bdc_number parameter'
                }, status=status.HTTP_400_BAD_REQUEST)
        else:
            if not user.active_company:
                return Response({
                    'detail': 'User must have an active company'
                }, status=status.HTTP_400_BAD_REQUEST)

            bdc_number = user.active_company.bdc_number
            if not bdc_number:
                return Response({
                    'detail': 'Your company does not have a BDC number configured'
                }, status=status.HTTP_400_BAD_REQUEST)

        company = Company.objects.filter(bdc_number=bdc_number).first()

        if not company:
            return Response({
                'detail': 'No company found with the provided BDC number',
            }, status=status.HTTP_404_NOT_FOUND)

        date_serializer = DateRangeSerializer(data=request.query_params)
        self.pagination_class = CallBDCLimitOffsetPagination
        calls_queryset = self.get_queryset().filter(transfer_number=bdc_number)

        if date_serializer.is_valid():
            start_date = date_serializer.validated_data.get('start_date')
            end_date = date_serializer.validated_data.get('end_date')

            if start_date and end_date:
                calls_queryset = calls_queryset.filter(
                    created_at__gte=start_date,
                    created_at__lte=end_date
                )

        filtered_queryset = self.filter_queryset(calls_queryset)
        calls_page = self.paginate_queryset(filtered_queryset)

        calls_data = []
        for call in calls_page:
            try:
                customer = Customer.objects.get(phone=call.from_number, company=call.company)
                customer_name = customer.name
            except Customer.DoesNotExist:
                customer_name = "Unknown"

            call_data = {
                "id": call.id,
                "call_datetime": call.created_at.isoformat(),
                "customer_name": customer_name,
                "customer_number": call.from_number,
                "sentiment": call.sentiment,
                "twilio_call_id": call.twilio_call_sid,
                "schedule_intention": call.booking_intent,
                "schedule_datetime": call.created_at.isoformat(),
                "call_summary": call.summary or "",
                "call_id": call.call_id,
                "transcript": call.transcript or ""
            }
            calls_data.append(call_data)

        return self.get_paginated_response(calls_data)

    @action(detail=False, methods=['get'], url_path='daily-calls-old')
    def daily_calls(self, request):
        """
        Return per-day unique and repetitive call counts for a given date range.
        Everything uses Django's timezone utilities.
        """
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        # drop time part
        start_date = validated["start_date"].date()
        end_date = validated["end_date"].date()

        filtered_queryset = self.filter_queryset(self.get_queryset())
        user = request.user

        company_tz = timezone.get_default_timezone()
        if getattr(user, "active_company", None) and getattr(user.active_company, "timezone", None):
            try:
                company_tz = timezone.pytz.timezone(user.active_company.timezone)
            except Exception:
                company_tz = timezone.get_default_timezone()

        # start_date = datetime.fromisoformat(start_date).date()
        # end_date = datetime.fromisoformat(end_date).date()

        # start_date = validated["start_date"].date()
        # end_date = validated["end_date"].date()

        start_dt = timezone.make_aware(datetime.combine(start_date, datetime.min.time()), company_tz)
        end_dt_exclusive = timezone.make_aware(
            datetime.combine(end_date + timedelta(days=1), datetime.min.time()),
            company_tz,
        )

        queryset_in_range = filtered_queryset.filter(
            created_at__gte=start_dt,
            created_at__lt=end_dt_exclusive,
        )

        daily_agg = (
            queryset_in_range
            .annotate(day=TruncDate("created_at", tzinfo=company_tz))
            .values("day")
            .annotate(
                total_calls=Count("id"),
                unique_callers=Count("from_number", distinct=True),
            )
            .order_by("day")
        )

        days_range = {
            start_date + timedelta(days=i): {"unique": 0, "repetitive": 0}
            for i in range((end_date - start_date).days + 1)
        }

        for row in daily_agg:
            local_day = row["day"]  # already in company tz
            unique = row["unique_callers"] or 0
            total = row["total_calls"] or 0
            days_range[local_day] = {"unique": unique, "repetitive": max(total - unique, 0)}

        result = [
            {"date": day.strftime("%b-%d"), **days_range[day]}
            for day in sorted(days_range.keys())
        ]

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

    @action(detail=False, methods=['get'], url_path='user-performance')
    def user_performance(self, request):
        """Return user performance statistics with call counts and success rates."""
        filtered_queryset = self.filter_queryset(self.get_queryset())
        user = request.user

        company_users = User.objects.filter(
            is_superuser=False,
            is_active=True,
            profile__isnull=False,
            profile__phone_number__isnull=False,
        ).exclude(profile__phone_number="")

        if not user.is_superuser and user.active_company:
            company_users = company_users.filter(companies=user.active_company)
        elif user.is_superuser:
            company_users = company_users[:5]
        else:
            return Response({
                'detail': 'No active company found',
            }, status=status.HTTP_400_BAD_REQUEST)

        user_stats = []
        colors = ["#e6e8b3", "#76c487", "#f4a261", "#e76f51", "#264653", "#2a9d8f", "#e9c46a"]

        for i, user_obj in enumerate(company_users):
            user_calls = filtered_queryset.filter(transfer_user=user_obj)

            total_calls = user_calls.count()
            successful_calls = user_calls.filter(transfer_status=TRANSFER_STATUS.SUCCESSFUL.value).count()
            missed_calls = user_calls.filter(transfer_status=TRANSFER_STATUS.FAILED.value).count()

            missed_percentage = 0.0
            if total_calls > 0:
                missed_percentage = round((missed_calls / total_calls) * 100, 1)

            background_color = colors[i % len(colors)]
            if missed_percentage > 50:
                background_color = "#e76f51"
            elif missed_percentage > 25:
                background_color = "#f4a261"
            else:
                background_color = "#76c487"

            user_stats.append({
                "user_id": user_obj.id,
                "username": user_obj.username,
                "total_calls": total_calls,
                "successful_calls": successful_calls,
                "missed_calls": missed_calls,
                "missed_percentage": missed_percentage,
                "background_color": background_color
            })

        user_stats = sorted(user_stats, key=lambda x: x["missed_percentage"], reverse=True)

        return Response(user_stats, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='worst-performing-users')
    def worst_performing_users(self, request):
        """Return users with worst performance based on missed calls."""
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        start_date = validated["start_date"].date()
        end_date = validated["end_date"].date()

        user = request.user

        company_users = User.objects.filter(
            is_superuser=False,
            is_active=True,
            profile__isnull=False,
            profile__phone_number__isnull=False,
        ).exclude(profile__phone_number="")

        if not user.is_superuser and user.active_company:
            company_users = company_users.filter(companies=user.active_company)
        elif user.is_superuser:
            company_users = company_users[:20]

        user_performance = []

        for company_user in company_users:
            user_calls = Call.objects.filter(
                transfer_user=company_user,
                created_at__date__range=[start_date, end_date]
            )

            total_calls = user_calls.count()
            if total_calls == 0:
                continue

            calls_attended = user_calls.filter(transfer_status=TRANSFER_STATUS.SUCCESSFUL.value).count()
            calls_missed = user_calls.filter(transfer_status=TRANSFER_STATUS.FAILED.value).count()

            avg_attended = (calls_attended / total_calls) * 100
            avg_missed = (calls_missed / total_calls) * 100

            user_performance.append({
                "username": f"{company_user.profile.first_name} {company_user.profile.last_name}",
                "calls_missed": calls_missed,
                "calls_attended": calls_attended,
                "average_missed": f"{avg_missed:.2f}%",
                "avg_attended": f"{avg_attended:.2f}%"
            })

        user_performance.sort(key=lambda x: x["calls_missed"], reverse=True)

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

    @action(detail=False, methods=['get'], url_path='best-performing-users')
    def best_performing_users(self, request):
        """Return users with best performance based on attended calls."""
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        start_date = validated["start_date"].date()
        end_date = validated["end_date"].date()

        user = request.user

        company_users = User.objects.filter(
            is_superuser=False,
            is_active=True,
            profile__isnull=False,
            profile__phone_number__isnull=False,
        ).exclude(profile__phone_number="")

        if not user.is_superuser and user.active_company:
            company_users = company_users.filter(companies=user.active_company)
        elif user.is_superuser:
            company_users = company_users[:10]

        user_performance = []

        for company_user in company_users:
            user_calls = Call.objects.filter(
                transfer_user=company_user,
                created_at__date__range=[start_date, end_date]
            )

            total_calls = user_calls.count()
            if total_calls == 0:
                continue

            calls_attended = user_calls.filter(transfer_status=1).count()  # Transferred
            calls_missed = user_calls.filter(transfer_status=2).count()  # Transfer Failed

            avg_attended = (calls_attended / total_calls) * 100
            avg_missed = (calls_missed / total_calls) * 100

            user_performance.append({
                "username": f"{company_user.profile.first_name} {company_user.profile.last_name}",
                "calls_missed": calls_missed,
                "calls_attended": calls_attended,
                "average_missed": f"{avg_missed:.2f}%",
                "avg_attended": f"{avg_attended:.2f}%",
                "total_calls": total_calls
            })

        user_performance.sort(key=lambda x: x["avg_attended"], reverse=True)

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

    @action(detail=False, methods=['get'], url_path='top-callers')
    def top_callers(self, request):
        """Return top callers based on call frequency."""
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        start_date = validated["start_date"].date()
        end_date = validated["end_date"].date()

        user = request.user

        base_queryset = Call.objects.filter(
            created_at__date__range=[start_date, end_date]
        )

        if not user.is_superuser and user.active_company:
            base_queryset = base_queryset.filter(company=user.active_company)
        elif not user.is_superuser:
            return Response({
                'message': 'No active company found',
                'data': []
            }, status=status.HTTP_200_OK)

        call_counts = (
            base_queryset.values('from_number')
            .annotate(call_count=Count('id'))
            .order_by('-call_count')[:10]
        )

        result = []
        for caller in call_counts:
            phone_number = caller['from_number']
            call_count = caller['call_count']

            most_recent_call = base_queryset.filter(
                from_number=phone_number
            ).order_by('-created_at').first()

            if most_recent_call:
                customer = Customer.objects.filter(
                    phone=most_recent_call.from_number,
                    company=user.active_company
                ).first()
                customer_name = customer.name if customer else "Unknown"

                duration_seconds = int(most_recent_call.duration)
                duration_formatted = f"{duration_seconds // 3600}:{(duration_seconds % 3600) // 60:02d}:{duration_seconds % 60:02d}"

                call_activities = most_recent_call.activities.order_by('-performed_at')
                activities_data = CallActivitySerializer(call_activities, many=True).data

                result.append({
                    "call_id": most_recent_call.id,
                    "twilio_call_id": most_recent_call.twilio_call_sid,
                    "call_date_time": most_recent_call.created_at.isoformat(),
                    "customer_name": customer_name,
                    "customer_number": phone_number,
                    "call_count": call_count,
                    "duration": duration_formatted,
                    "transcript": most_recent_call.transcript,
                    "partner_call_id": most_recent_call.call_id,
                    "sentiment": most_recent_call.sentiment,
                    "call_summary": most_recent_call.summary,
                    "call_activities": activities_data
                })

        return Response(result, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='hourly-calls')
    def hourly_calls(self, request):
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        start_date = validated["start_date"].date()
        end_date = validated["end_date"].date()

        filtered_queryset = self.filter_queryset(self.get_queryset())
        user = request.user

        company_tz = timezone.get_default_timezone()
        if getattr(user, "active_company", None) and getattr(user.active_company, "timezone", None):
            try:
                company_tz = timezone.pytz.timezone(user.active_company.timezone)
            except Exception:
                company_tz = timezone.get_default_timezone()

        start_dt = timezone.make_aware(datetime.combine(start_date, datetime.min.time()), company_tz)
        end_dt_exclusive = timezone.make_aware(
            datetime.combine(end_date + timedelta(days=1), datetime.min.time()),
            company_tz,
        )

        queryset_in_range = filtered_queryset.filter(
            created_at__gte=start_dt,
            created_at__lt=end_dt_exclusive,
        )

        hourly_agg = (
            queryset_in_range
            .annotate(local_hour=ExtractHour(TruncHour("created_at", tzinfo=company_tz)))
            .values("local_hour")
            .annotate(calls=Count("id"))
            .order_by("local_hour")
        )

        hours_range = {h: 0 for h in range(24)}

        for row in hourly_agg:
            hour = row["local_hour"]
            calls = row["calls"] or 0
            hours_range[hour] = calls

        result = [
            {"time": f"{hour:02d}:00", "calls": calls}
            for hour, calls in hours_range.items()
        ]

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

    @action(detail=False, methods=['get'], url_path='monthly-stats')
    def monthly_stats(self, request):
        """Return monthly call statistics with total calls and missed percentage for current year only."""
        filtered_queryset = self.filter_queryset(self.get_queryset())
        user = request.user

        company_tz = timezone.get_default_timezone()
        if getattr(user, "active_company", None) and getattr(user.active_company, "timezone", None):
            try:
                company_tz = timezone.pytz.timezone(user.active_company.timezone)
            except Exception:
                company_tz = timezone.get_default_timezone()

        # Filter for current year only
        current_year = timezone.now().year
        start_of_year = timezone.make_aware(
            datetime(current_year, 1, 1), company_tz
        )
        end_of_year = timezone.make_aware(
            datetime(current_year + 1, 1, 1), company_tz
        )

        # current_year_queryset = filtered_queryset.filter(
        #     created_at__gte=start_of_year,
        #     created_at__lt=end_of_year
        # )

        monthly_agg = (
            filtered_queryset
            .annotate(
                year=Extract('created_at', 'year', tzinfo=company_tz),
                month=Extract('created_at', 'month', tzinfo=company_tz)
            )
            .values('year', 'month')
            .annotate(
                total_calls=Count('id'),
                not_transferred_calls=Count('id', filter=Q(transfer_status=0)),
                received_calls=Count('id', filter=Q(transfer_status=1)),
                missed_calls=Count('id', filter=Q(transfer_status=2)),
                avg_call_time=Round(Avg('duration')),
                tot_call_time=Sum('duration')
            )
            .filter(total_calls__gt=0)  # Only include months with calls
            .order_by('month')
        )

        month_names = {
            1: "January", 2: "February", 3: "March", 4: "April",
            5: "May", 6: "June", 7: "July", 8: "August",
            9: "September", 10: "October", 11: "November", 12: "December"
        }

        result = []
        for row in monthly_agg:
            month_num = int(row['month'])
            month_name = month_names.get(month_num, f"Month {month_num}")
            total_calls = row['total_calls'] or 0
            missed_calls = row['missed_calls'] or 0
            received_calls = row['received_calls'] or 0
            avg_call_time = row['avg_call_time'] or 0
            tot_call_time = row['tot_call_time'] or 0
            not_transferred_calls = row['not_transferred_calls'] or 0
            year = row['year']

            year = int(row['year'])
            start_of_month = timezone.make_aware(
                datetime(year, month_num, 1), company_tz
            )

            end_of_month = start_of_month + relativedelta(months=1)
            end_of_month = end_of_month - timedelta(microseconds=1)

            # missed_percentage = "0%"
            # if total_calls > 0:
            #     missed_percentage = f"{round((missed_calls / total_calls) * 100)}%"

            result.append({
                "month": month_name,
                "year": year,
                "start_date": start_of_month,
                "end_date": end_of_month,
                "tot_calls": total_calls,
                "not_transferred_calls": not_transferred_calls,
                "missed_calls": missed_calls,
                "received_calls": received_calls,
                "avg_call_time": avg_call_time,
                "tot_call_time": tot_call_time
            })

        return Response(result, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='transfer-percentage')
    def transfer_percentage(self, request):
        """Return percentages for transferred, not transferred, failed, and unknown statuses."""
        serializer = DateRangeSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        validated = serializer.validated_data

        start_date = validated["start_date"]
        end_date = validated["end_date"]

        filtered_queryset = self.filter_queryset(self.get_queryset())
        calls_in_range = filtered_queryset.filter(
            created_at__gte=start_date,
            created_at__lte=end_date
        )

        total_calls = calls_in_range.count()

        if total_calls == 0:
            return Response({
                "transferred_percentage": 0.0,
                "not_transferred_percentage": 0.0,
                "failed_percentage": 0.0,
                "unknown_percentage": 0.0,
                "total_calls": 0
            }, status=status.HTTP_200_OK)

        transferred_count = calls_in_range.filter(transfer_status=1).count()  # SUCCESSFUL
        not_transferred_count = calls_in_range.filter(transfer_status=0).count()  # NOT_TRANSFERRED
        failed_count = calls_in_range.filter(transfer_status=2).count()  # FAILED
        unknown_count = calls_in_range.exclude(transfer_status__in=[0, 1, 2]).count()  # Any other status

        transferred_percentage = round((transferred_count / total_calls) * 100, 2)
        not_transferred_percentage = round((not_transferred_count / total_calls) * 100, 2)
        failed_percentage = round((failed_count / total_calls) * 100, 2)
        unknown_percentage = round((unknown_count / total_calls) * 100, 2)

        return Response({
            "transferred_percentage": transferred_percentage,
            "not_transferred_percentage": not_transferred_percentage,
            "failed_percentage": failed_percentage,
            "unknown_percentage": unknown_percentage,
        }, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='process-booking-intent')
    def process_booking_intent(self, request):
        """Process booking intent for a call."""
        # call_id = request.query_params.get('call_id')
        # call = Call.objects.get(id=call_id)
        # if not call:
        #     return Response({"message": "Call not found"}, status=status.HTTP_404_NOT_FOUND)
        # process_booking_intent(call_id)
        print("process_booking_intent function removed from api process-booking-intent")
        return Response({"message": "Booking intent processed converted into trigger task"}, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'], url_path='read')
    def read_call(self, request):
        """Process booking intent for a call and mark as read."""
        serializer = ReadCallSerializer(data=request.query_params, context={'request': request})

        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        id = serializer.validated_data['id']
        user = request.user

        if user.is_superuser:
            call = Call.objects.get(id=id)
            current_time = timezone.now()

            call.read_by = user
            call.read_at = current_time

            CallActivity.objects.create(
                call=call,
                user=request.user,
                action='READ',
                performed_at=current_time,
            )

            call.save()

            return Response({
                "detail": "Call marked as read from super admin.",
            }, status=status.HTTP_200_OK)

        call = Call.objects.get(id=id, company=user.active_company)

        company_timezone = pytz.timezone(user.active_company.timezone)
        current_time_utc = timezone.now()
        current_time_company = current_time_utc.astimezone(company_timezone)

        call.read_by = user
        call.read_at = current_time_company

        CallActivity.objects.create(
            call=call,
            user=request.user,
            action='READ',
            performed_at=current_time_company,
        )

        call.save()

        return Response({
            "detail": "Call marked as read.",
        }, status=status.HTTP_200_OK)

    @action(detail=True, methods=['get'], url_path='get-recording')
    def download_recording(self, request, pk=None):
        call = self.get_object()

        recording_service = RecordingService(get_twilio_client())
        storage_service = StorageService()

        filename = storage_service.build_call_audio_filename(call)
        relative_path, filepath = storage_service.call_recording_local_path(filename)

        if os.path.exists(filepath):
            return Response({"recording_url": storage_service.call_recording_public_url(request, relative_path)})

        recording = recording_service.get_latest_call_recording(call.twilio_call_sid)
        response = recording_service.download(recording.sid)
        storage_service.save_call_recording_stream(filepath, response)

        return Response({"recording_url": storage_service.call_recording_public_url(request, relative_path)})

    ## ========================= WEB HOOKS ===========================

    @action(detail=False,
            methods=['post'],
            url_path='record-offtime-message',
            permission_classes=[AllowAny],
            authentication_classes=[]
            )
    def record_offtime_message(self, request):
        """Handle incoming webhook requests."""
        webhook_data = request.data
        call_data = webhook_data.get('call', {})
        customer_phone = call_data.get('from_number')
        company_phone = call_data.get('to_number')

        logger.info("=============================== Record Offtime Message request.data starts ===================================")
        logger.info(f"Webhook Data: {webhook_data}")

        args = webhook_data.get('args', {})
        advisor_id = args.get('advisor_id')
        advisor_name = args.get('advisor_name')
        customer_name = args.get('customer_name')
        customer_note = args.get('customer_note')
        
        logger.info(f"type of advisor_id: {type(advisor_id)}")
        logger.info(f"advisor_id: {advisor_id}")
        logger.info(f"advisor_name: {advisor_name}")
        logger.info(f"customer_name: {customer_name}")
        logger.info(f"customer_note: {customer_note}")


        logger.info("=============================== Getting Company Bot Settings ===================================")
        companyBotSettings = CompanyBotSettings.objects.filter(phone_number=company_phone).first()
        company = companyBotSettings.company if companyBotSettings else None

        if not company:
            return Response({
                "detail": "Company not found"
            }, status=status.HTTP_404_NOT_FOUND)
        
        user = company.users.filter(id=advisor_id, is_active=True).first()

        if not user:
            return Response({
                "detail": "User not found"
            }, status=status.HTTP_404_NOT_FOUND)

        logger.info("=============================== Creating User Message ===================================")
        UserMessage.objects.create(
            recipient=user,
            body=customer_note,
            customer_name=customer_name,
            customer_number=customer_phone,
            subject=f"Offtime Message from {customer_name}"
        )
        logger.info("=============================== User Message created successfully ===================================")
        
        logger.info("=============================== Recording Offtime Message ===================================")        
        return Response({"message": "Offtime message recorded successfully"}, status=status.HTTP_200_OK)


    @action(detail=False,
            methods=['post'],
            url_path='send-appointment-email',
            permission_classes=[AllowAny],
            authentication_classes=[]
            )
    def send_appointment_email(self, request):
        """Handle incoming webhook requests."""
        webhook_data = request.data
        call_data = webhook_data.get('call', {})
        customer_phone = call_data.get('from_number')
        company_phone = call_data.get('to_number')

        logger.info("=============================== Send Appointment Email request.data starts ===================================")
        logger.info(f"Webhook Data: {webhook_data}")

        args = webhook_data.get('args', {})
        booking_datetime = args.get('booking_datetime')
        customer_name = args.get('customer_name')
        service = args.get('service')
        
        logger.info(f"booking_datetime: {booking_datetime}")
        logger.info(f"customer_name: {customer_name}")
        logger.info(f"service: {service}")


        logger.info("=============================== Getting Company Bot Settings ===================================")
        companyBotSettings = CompanyBotSettings.objects.filter(phone_number=company_phone).first()
        company = companyBotSettings.company if companyBotSettings else None

        if not company:
            return Response({
                "detail": "Company not found"
            }, status=status.HTTP_404_NOT_FOUND)
        
        logger.info("=============================== Getting BDC number ===================================")
        ## Get bdc number from company
        bdc_number = company.bdc_number
        if not bdc_number:
            return Response({
                "detail": "BDC number not found"
            }, status=status.HTTP_404_NOT_FOUND)
        
        logger.info("=============================== Sending Email and SMS ===================================")
        send_booking_email(bdc_number, customer_name, booking_datetime, service, customer_phone)
        send_booking_sms(bdc_number, customer_name, booking_datetime, service, customer_phone)
        
        logger.info("=============================== Email and SMS sent successfully ===================================")
        
        return Response({"message": "Email and SMS sent successfully"}, status=status.HTTP_200_OK)

    @action(detail=False,
            methods=['post'],
            url_path='log-call-webhook',
            permission_classes=[AllowAny],
            authentication_classes=[]
            )
    def log_call_webhook(self, request):
        """Handle incoming webhook requests."""
        webhook_data = request.data
        event = webhook_data.get('event')

        if event == 'call_analyzed':
            call_data = webhook_data.get('call', {})

            customer_phone = call_data.get('from_number')
            company_phone = call_data.get('to_number')
            is_call_transferred = call_data.get('disconnection_reason') == 'call_transfer'
            logger.info("=-=-=-=-=-=-=-=Call Data=-=-=-=-=-=-=-=")
            logger.info(f"Customer Phone: {customer_phone}")
            logger.info(f"Company Phone: {company_phone}")
            logger.info(f"Is Call Transferred: {is_call_transferred}")
            logger.info(f"Call Data: {call_data}")
            logger.info("=-=-=-=-=-=-=-=Call Data=-=-=-=-=-=-=-=")
            companyBotSettings = CompanyBotSettings.objects.filter(phone_number=company_phone).first()
            company = companyBotSettings.company if companyBotSettings else None

            botType = BotType.SALES_BOT.value if companyBotSettings.bot_name.lower() == 'hazel' else BotType.SERVICE_BOT.value if companyBotSettings.bot_name.lower() == 'maya' else None

            transfer_destination_number = None
            transcript_with_tool_calls = call_data.get('transcript_with_tool_calls', [])
            for item in transcript_with_tool_calls:
                if item.get('role') == 'tool_call_invocation' and item.get('name') == 'transfer_call':
                    arguments = item.get('arguments', '{}')
                    args_data = json.loads(arguments)
                    transfer_destination_number = args_data.get('number')
            user = None
            if (transfer_destination_number):
                user_profile = UserProfile.objects.filter(phone_number=transfer_destination_number).first()
                user = user_profile.user if user_profile else None

            transfer_status = TRANSFER_STATUS.NOT_TRANSFERRED.value

            if transfer_destination_number and is_call_transferred:
                transfer_status = TRANSFER_STATUS.SUCCESSFUL.value
            elif transfer_destination_number and not is_call_transferred:
                transfer_status = TRANSFER_STATUS.FAILED.value

            sentiment_str = call_data.get('call_analysis', {}).get('user_sentiment', '')
            if isinstance(sentiment_str, tuple) and sentiment_str:
                sentiment_str = sentiment_str[0]
            sentiment_str = str(sentiment_str).strip("'")
            sentiment = SENTIMENT.NEGATIVE.value
            if sentiment_str.lower() == 'positive':
                sentiment = SENTIMENT.POSITIVE.value
            elif sentiment_str.lower() == 'neutral':
                sentiment = SENTIMENT.NEUTRAL.value
            twilio_call_sid = call_data.get('telephony_identifier', {}).get('twilio_call_sid')

            call_analysis = call_data.get("call_analysis", {})
            custom_analysis = call_analysis.get("custom_analysis_data", {})
            customer_name = custom_analysis.get("customer_name")
            call_time_category = custom_analysis.get("call_time_category")
            clean_customer_name = (
                customer_name.strip() if isinstance(customer_name, str) and customer_name.strip() else None
            )

            logger.info(f"Customer Name: {customer_name}")
            logger.info(f"Clean Customer Phone: {clean_customer_name}")

            call, call_created = Call.objects.update_or_create(
                call_id=call_data.get('call_id'),
                defaults={
                    'from_number': customer_phone,
                    'to_number': company_phone,
                    'caller_name': clean_customer_name,
                    'company': company,
                    'transfer_number': transfer_destination_number,
                    'call_time_category': call_time_category,
                    'transfer_user': user,
                    'transfer_status': transfer_status,
                    'bot_type': botType,
                    'status': call_data.get('call_status'),
                    'transcript': call_data.get('transcript'),
                    'sentiment': sentiment,
                    'summary': call_data.get('call_analysis', {}).get('call_summary'),
                    'duration': call_data.get('call_cost', {}).get('total_duration_seconds') or 0,
                    'cost': call_data.get('call_cost', {}).get('combined_cost') or 0,
                    'twilio_call_sid': twilio_call_sid,
                }
            )



            appointment = Appointment.objects.filter(twilio_call_sid=twilio_call_sid).first()
            if appointment:
                appointment.call = call
                appointment.save()

            customer_defaults = {
                'last_call_at': timezone.now(),
                'last_call_id': call.id,
            }

            if clean_customer_name:
                customer_defaults['name'] = customer_name.strip()

            customer, created = Customer.objects.get_or_create(
                phone=customer_phone,
                company=company,
                defaults=customer_defaults
            )

            if created:
                customer.name_extracted = get_caller_name_by_phone_number(customer_phone)
                customer.save(update_fields=["name_extracted"])

            if call_created and transfer_destination_number is not None:
                process_booking_intent.apply_async(
                    kwargs={"call_id": call.id, "use_speaker": True},
                    countdown=30,
                )

                if transfer_status == TRANSFER_STATUS.FAILED.value:
                    # Sends notifications to advisor that he have missed the call from customer name along with phone number
                    # Sends notifications to all the chief admins and service managers of that company
                    NotificationService.send_missed_call_notification(
                        call=call,
                        user=user,
                        company=company,
                        customer_name=clean_customer_name,
                        customer_phone=customer_phone
                    )

            action = "created" if call_created else "updated"
            logger.info(f"Call {call_created} successfully: {call.call_id}")

            return Response({
                'message': f'Call {action} successfully',
                'call_id': call.call_id,
                'action': action
            }, status=status.HTTP_200_OK)
        else:
            logger.info(f"Call webhook for {event} received successfully")
            return Response({
                'message': f'Call webhook for {event} received successfully',
                'event': event,
                'data': webhook_data
            }, status=status.HTTP_200_OK)

    @action(
        methods=['post'],
        detail=False,
        url_path='inbound-dynamic-variables-webhook',
        permission_classes=[AllowAny],
        authentication_classes=[]
    )
    def inbound_dynamic_variables_webhook(self, request):
        """Handle incoming webhook requests."""
        to_number = None
        if request.data.get('event') == 'call_inbound' and 'call_inbound' in request.data:
            to_number = request.data['call_inbound'].get('to_number')

        logger.info(f"Extracted company_number: {to_number}")

        bot_name = get_bot_name(to_number)
        if not bot_name:
            return Response({"detail": "This bot not configured in our system."}, status=status.HTTP_400_BAD_REQUEST)


        overrideAgentId = None
        dynamic_variables = None
        if bot_name.lower() == "hazel":
            # dynamic_variables = {
            #     'is_off_hours': 'false',
            #     'timezone_offset': '0',
            #     }
            sales_info = check_sales_time_by_phone(to_number)
            is_holiday = sales_info['is_holiday']
            is_office_hours = sales_info['is_office_hours']
            dynamic_variables = {k: str(v) for k, v in sales_info.items()}
            if is_holiday:
                overrideAgentId = "agent_f72f27787dfcdcd7dc94ed136a" # Lyra
            elif is_office_hours is False:
                overrideAgentId = "agent_9969e3fc1fd45a66a76ef1913b" # Olivia

        elif bot_name.lower() == "maya":
            company_info = get_company_working_info(to_number)
            is_holiday = company_info['is_holiday']
            is_office_hours = company_info['is_office_hours']

            if is_holiday:
                overrideAgentId = "agent_f72f27787dfcdcd7dc94ed136a" # Lyra
            elif is_office_hours is False:
                overrideAgentId = "agent_9969e3fc1fd45a66a76ef1913b" # Olivia


            dynamic_variables = {k: str(v) for k, v in company_info.items()}

        # if dynamic_variables is not None:
        #     return Response({
        #         "call_inbound": {
        #             "dynamic_variables": dynamic_variables,
        #         }
        #     }, status=status.HTTP_200_OK)

        if dynamic_variables is not None:
            call_inbound_payload = {
                "dynamic_variables": dynamic_variables
            }

            if overrideAgentId is not None:
                call_inbound_payload["override_agent_id"] = overrideAgentId

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

        return Response({"detail": "This bot not configured in our system."}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False,
            methods=['post'],
            url_path='log-transfer-call-webhook',
            permission_classes=[AllowAny],
            authentication_classes=[]
            )
    def log_transfer_call_webhook(self, request):
        """Handle incoming webhook requests."""
        logger.info(f"Log Transfer Call Webhook received - Data: {request.data}")
        logger.info(f"Log Transfer Call Webhook - Headers: {dict(request.headers)}")
        logger.info(f"Log Transfer Call Webhook - Method: {request.method}")

        return Response({'message': 'Webhook received successfully'}, status=status.HTTP_200_OK)
        """Get detailed call information with filtering options."""
        try:
            # Get query parameters
            page = int(request.query_params.get('page', 1))
            page_size = int(request.query_params.get('page_size', 20))
            start_date_str = request.query_params.get('start_date')
            end_date_str = request.query_params.get('end_date')
            from_number = request.query_params.get('from_number')
            to_number = request.query_params.get('to_number')
            call_status = request.query_params.get('call_status')
            transfer_status = request.query_params.get('transfer_status')
            company_id = request.query_params.get('company_id')

            # Build the base queryset
            queryset = Call.objects.select_related('company').all()

            # Apply filters
            if start_date_str and end_date_str:
                try:
                    from datetime import datetime
                    start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
                    end_date = datetime.strptime(end_date_str, '%Y-%m-%d')
                    queryset = queryset.filter(created_at__gte=start_date, created_at__lte=end_date)
                except ValueError:
                    return Response({
                        'message': 'Invalid date format',
                        'error': 'Dates must be in YYYY-MM-DD format'
                    }, status=status.HTTP_400_BAD_REQUEST)

            if from_number:
                queryset = queryset.filter(from_number__icontains=from_number)

            if to_number:
                queryset = queryset.filter(to_number__icontains=to_number)

            if call_status:
                queryset = queryset.filter(status=call_status)

            if transfer_status is not None:
                queryset = queryset.filter(transfer_status=int(transfer_status))

            if company_id:
                queryset = queryset.filter(company_id=company_id)

            # Order by most recent first
            queryset = queryset.order_by('-created_at')

            # Pagination
            from django.core.paginator import Paginator
            paginator = Paginator(queryset, page_size)

            try:
                page_obj = paginator.page(page)
            except:
                return Response({
                    'message': 'Invalid page number',
                    'error': f'Page {page} does not exist'
                }, status=status.HTTP_400_BAD_REQUEST)

            # Serialize the data
            calls_data = []
            for call in page_obj:
                call_data = {
                    'id': call.id,
                    'call_id': call.call_id,
                    'from_number': call.from_number,
                    'to_number': call.to_number,
                    'status': call.status,
                    'transfer_status': call.transfer_status,
                    'transfer_number': getattr(call, 'transfer_number', None),
                    'transfer_user': getattr(call, 'transfer_user', None),
                    'transcript': call.transcript,
                    'summary': call.summary,
                    'duration': float(call.duration) if call.duration else 0,
                    'cost': float(call.cost) if call.cost else 0,
                    'twilio_call_sid': call.twilio_call_sid,
                    'created_at': call.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                    'updated_at': call.updated_at.strftime('%Y-%m-%d %H:%M:%S'),
                    'company': {
                        'id': call.company.id,
                        'name': call.company.company_name,
                        'phone': call.company.company_phone
                    } if call.company else None
                }
                calls_data.append(call_data)

            return Response({
                'message': 'Call details retrieved successfully',
                'data': calls_data,
                'pagination': {
                    'current_page': page,
                    'page_size': page_size,
                    'total_pages': paginator.num_pages,
                    'total_count': paginator.count,
                    'has_next': page_obj.has_next(),
                    'has_previous': page_obj.has_previous()
                },
                'filters': {
                    'start_date': start_date_str,
                    'end_date': end_date_str,
                    'from_number': from_number,
                    'to_number': to_number,
                    'call_status': call_status,
                    'transfer_status': transfer_status,
                    'company_id': company_id
                }
            }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f"Error getting call details: {str(e)}")
            return Response({
                'message': 'Error retrieving call details',
                'error': str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
