import logging
from celery import shared_task, group
from celery.exceptions import SoftTimeLimitExceeded
from utils.logger import log_step
from django.db import transaction
from django.db.models.functions import Trim
from apps.companies.models import Company
from apps.calls.models import Call
from apps.calls.constants import CRON_JOB_STATUS, BotType
from apps.calls.services import (
    RecordingService,
    get_twilio_client,
    get_speech_to_text,
)

logger = logging.getLogger(__name__)


@shared_task(
    bind=True,
    max_retries=3,
    default_retry_delay=60,
    name='apps.calls.tasks.process_all_booking_intents_task'
)
def process_all_booking_intents_task(self):
    """
    Process booking intent for all companies having BDC numbers.
    """
    try:
        bdc_numbers = list(
            Company.objects
                .exclude(bdc_number__isnull=True)
                .annotate(clean_bdc=Trim('bdc_number'))
                .filter(clean_bdc__regex=r'^\+?\d+$')
                .values_list('clean_bdc', flat=True)
                .distinct()
        )

        if not bdc_numbers:
            return

        calls_to_process = (
            Call.objects.filter(
                bot_type=BotType.SERVICE_BOT.value,
                company__isnull=False,
            )
            .exclude(
                booking_intent_status=CRON_JOB_STATUS["COMPLETED"]
            )
            # .exclude(
            #     booking_intent_status=CRON_JOB_STATUS["PROCESSING"]
            # )
            .only("id", "booking_intent_status")
            .distinct()
        )

        call_tasks = [
            process_booking_intent.s(call.id) for call in calls_to_process.iterator(
                chunk_size=50
            )]
        group(call_tasks).apply_async()

    except Exception as exc:
        logger.exception(f"Failed to enqueue calls: {exc}")
        raise self.retry(exc=exc)

@shared_task(
    bind=True,
    max_retries=3,
    default_retry_delay=60,
    soft_time_limit=300
)
def process_booking_intent(self, call_id, use_speaker=False):
    """
    Process booking intent for a single call.
    """
    call = None
    try:
        with transaction.atomic():
            call = Call.objects.select_for_update().filter(id=call_id).first()
        if not call:
            logger.info(f"Call {call_id} not found.")
            return
        
        retry_count = self.request.retries
        
        twilio_call_id = call.twilio_call_sid
        if twilio_call_id is None:
            logger.info(f"No Twilio call ID for call {call.id}")
            return

        call.booking_intent_status = CRON_JOB_STATUS['PROCESSING']
        call.save(update_fields=['booking_intent_status'])
        
        recording_url = RecordingService(
            get_twilio_client()
        ).get_call_recording_public_url(twilio_call_id)

        if not recording_url:
            log_step("NO_RECORDING_FOUND", twilio_call_id)

            if retry_count >= 2:
                call.booking_intent_status = CRON_JOB_STATUS['FAILED']
                call.save(update_fields=['booking_intent_status'])
                logger.warning(f"Call {call_id}: No recording after 3 tries → FAILED")
                return

            raise self.retry(countdown=60, exc=Exception("Recording missing."))

        (
            transcript,
            booking_intent,
            booking_datetime,
            transcript_complete,
            _,
        )= get_speech_to_text(twilio_call_id, use_speaker=use_speaker)

        if not transcript or not transcript.strip():
            log_step("TRANSCRIPT_EMPTY", twilio_call_id)

            if retry_count >= 2:
                call.booking_intent_status = CRON_JOB_STATUS['FAILED']
                call.save(update_fields=['booking_intent_status'])
                logger.warning(f"Call {call_id}: Transcript empty after 3 tries → FAILED")
                return

            raise self.retry(countdown=60, exc=Exception("Transcript empty."))

        call.booking_intent = booking_intent
        call.twilio_recording_text = transcript
        call.booking_datetime = booking_datetime
        call.transcript_complete = get_transcript_complete_text(transcript_complete)
        call.booking_intent_status = CRON_JOB_STATUS['COMPLETED']
        call.save()

    except SoftTimeLimitExceeded:
        logger.warning(f"Soft time limit exceeded for call {call_id}. Marking as failed.")
        if call:
            call.booking_intent_status = CRON_JOB_STATUS['FAILED']
            call.save(update_fields=['booking_intent_status'])
    except Exception as exc:
        retry_count = self.request.retries

        if call and retry_count >= 2:
            call.booking_intent_status = CRON_JOB_STATUS['FAILED']
            call.save(update_fields=['booking_intent_status'])
            logger.warning(f"Call {call_id}: Failed after 3 tries → FINAL FAIL")
            return

        logger.exception(f"Unexpected error processing call {call_id}: {exc}")
        raise self.retry(exc=exc)

def get_transcript_complete_text(transcript_complete):
    final_label_text = ""
    if transcript_complete:
        for u in transcript_complete:
            if u.speaker == "A":
                name = "Agent: "
            elif u.speaker == "B":
                name = "User: "
            elif u.speaker == "C":
                name = "Advisor: "
            else:
                continue
            final_label_text += f"{name}{u.text}\n"
            print("u.speaker--:", u.speaker)
            print("u.text--:", u.text)

    return final_label_text

def test_process_booking_intent(call_id):
    call = Call.objects.filter(id=call_id).first()

    try:
        (
            transcript,
            booking_intent,
            booking_datetime,
            transcript_complete,
            results,
        ) = get_speech_to_text(
            Call.objects.filter(id=call_id)
            .first().twilio_call_sid,
            True
        )

        final_label_text = get_transcript_complete_text(transcript_complete)
        call.transcript_complete = final_label_text
        call.save()

        print("retell transcript :", call.transcript)
        print("assembly transcript --:", transcript)
        print("final_label_text:", final_label_text)
        print("transcript_complete from call--:", call.transcript_complete)
        print("booking_intent final--:", booking_intent)
        print("booking_datetime final--:", booking_datetime)
        print("results final--:", results)
        print("---------------------------")
        print("Process Complete Successfully for call id:", call_id)
        return

    except Exception as exc:
        logger.exception(f"Unexpected error processing call {call_id}: {exc}")
