from django.db import transaction, IntegrityError
from apps.companies.models import CallCatchPhrase, CatchPhrase
from apps.calls.models import Call
from typing import List, Dict
import re

import logging
logger = logging.getLogger(__name__)


SPEAKER_PATTERN = re.compile(r"^(Agent|User|Advisor):\s*(.+)$", re.I)


def phrase_matches(text: str, pattern: re.Pattern) -> bool:
    return bool(pattern.search(text.lower()))

def parse_transcript(transcript: str) -> List[Dict]:
    segments = []
    for line in transcript.splitlines():
        match = SPEAKER_PATTERN.match(line.strip())
        if not match:
            continue
        speaker, text = match.groups()
        segments.append({
            "speaker": speaker.lower(),
            "text": text.strip()
        })
    return segments

class CatchPhraseFillService:

    @staticmethod
    def fill_call(call: Call) -> int:

        if CallCatchPhrase.objects.filter(call=call).exists():
            logger.info(
                f"Skipping exits calls."
            )
            return 0

        if not call.transcript:
            logger.info(
                f"Skipping no transcript calls."
            )
            return 0

        phrases = CatchPhrase.objects.filter(company=call.company, is_active=True)

        phrase_patterns = []
        for phrase in phrases:
            use_word_boundary = phrase.match_type == "word"
            candidates = [phrase.phrase] + phrase.synonyms
            compiled_patterns = [
                re.compile(
                    (fr"\b{re.escape(c.lower())}\b" if use_word_boundary else re.escape(c.lower()))
                )
                for c in candidates
            ]
            phrase_patterns.append((phrase, compiled_patterns))

        segments = parse_transcript(call.transcript)
        new_objects = []

        for segment in segments:
            speaker = segment["speaker"]
            text = segment["text"]

            for phrase, patterns in phrase_patterns:
                if phrase.allowed_speakers and speaker not in phrase.allowed_speakers:
                    continue
                if any(phrase_matches(text, p) for p in patterns):
                    new_objects.append(
                        CallCatchPhrase(
                            call=call,
                            phrase=phrase,
                            company=call.company,
                            speaker=speaker,
                            confidence=1.0,
                            source="string",
                            matched_text=text
                        )
                    )

        if new_objects:
            try:
                with transaction.atomic():
                    CallCatchPhrase.objects.bulk_create(new_objects)
            except IntegrityError as e:
                logger.warning(
                    f"IntegrityError on call {call.id}: {e}. "
                    f"Skipping conflicting phrases and continuing."
                )
                return 0

        return len(new_objects)
