"""
Transcript comparison system for Trax platform.

Compares original and enhanced transcripts to identify improvements,
changes, and quality differences between versions.
"""

import re
import logging
from dataclasses import dataclass
from typing import Dict, List, Any, Optional, Protocol, Tuple
from enum import Enum

from ..base.services import BaseService

logger = logging.getLogger(__name__)


class ChangeType(Enum):
    """Types of changes between transcript versions."""
    ADDITION = "addition"
    DELETION = "deletion"
    MODIFICATION = "modification"
    CORRECTION = "correction"
    FORMATTING = "formatting"
    NO_CHANGE = "no_change"


@dataclass
class SegmentChange:
    """Represents a change between transcript segments."""
    segment_index: int
    original_text: str
    enhanced_text: str
    change_type: ChangeType
    confidence_improvement: float
    word_count_change: int
    changes_summary: List[str]
    start_time: float
    end_time: float
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "segment_index": self.segment_index,
            "original_text": self.original_text,
            "enhanced_text": self.enhanced_text,
            "change_type": self.change_type.value,
            "confidence_improvement": self.confidence_improvement,
            "word_count_change": self.word_count_change,
            "changes_summary": self.changes_summary,
            "start_time": self.start_time,
            "end_time": self.end_time
        }


@dataclass
class ComparisonResult:
    """Result of transcript comparison."""
    total_segments: int
    segments_with_changes: int
    overall_improvement_score: float
    segment_changes: List[SegmentChange]
    summary_statistics: Dict[str, Any]
    quality_metrics: Dict[str, float]
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "total_segments": self.total_segments,
            "segments_with_changes": self.segments_with_changes,
            "overall_improvement_score": self.overall_improvement_score,
            "segment_changes": [change.to_dict() for change in self.segment_changes],
            "summary_statistics": self.summary_statistics,
            "quality_metrics": self.quality_metrics
        }


class TranscriptComparerProtocol(Protocol):
    """Protocol for transcript comparison services."""
    def compare_transcripts(self, original: List[Dict[str, Any]], enhanced: List[Dict[str, Any]]) -> ComparisonResult: ...
    def calculate_similarity(self, text1: str, text2: str) -> float: ...
    def identify_changes(self, original_text: str, enhanced_text: str) -> List[str]: ...


class TranscriptComparer(BaseService):
    """Compare original and enhanced transcripts."""
    
    def __init__(self):
        super().__init__("TranscriptComparer")
        
        # Similarity thresholds
        self.high_similarity_threshold = 0.9
        self.medium_similarity_threshold = 0.7
        self.low_similarity_threshold = 0.5
        
        # Change detection patterns
        self.change_patterns = {
            "correction": [
                r'\b(um|uh|er|ah)\b',  # Filler words
                r'\b(like|you know|i mean)\b',  # Speech fillers
                r'\.\.\.',  # Ellipsis
                r'\(inaudible\)',  # Inaudible markers
                r'\(unintelligible\)',  # Unintelligible markers
            ],
            "formatting": [
                r'[A-Z]{2,}',  # Acronyms
                r'\b[A-Z][a-z]+ [A-Z][a-z]+\b',  # Proper nouns
                r'\d+\.\d+\.\d+',  # Version numbers
                r'[A-Za-z]+\.[A-Za-z]+',  # Domain names
            ],
            "technical": [
                r'\b[A-Z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*\b',  # CamelCase
                r'\b[a-z]+_[a-z]+(_[a-z]+)*\b',  # snake_case
                r'\b[A-Za-z0-9]+\([^)]*\)\b',  # Function calls
                r'[A-Za-z]+://[^\s]+',  # URLs
            ]
        }
        
        logger.info("TranscriptComparer initialized")
    
    def compare_transcripts(
        self, 
        original: List[Dict[str, Any]], 
        enhanced: List[Dict[str, Any]]
    ) -> ComparisonResult:
        """Compare original and enhanced transcripts."""
        if len(original) != len(enhanced):
            logger.warning("Original and enhanced transcripts have different segment counts")
        
        segment_changes = []
        total_improvement = 0.0
        segments_with_changes = 0
        
        # Compare segments
        for i in range(min(len(original), len(enhanced))):
            original_segment = original[i]
            enhanced_segment = enhanced[i]
            
            change = self._compare_segment(original_segment, enhanced_segment, i)
            segment_changes.append(change)
            
            if change.change_type != ChangeType.NO_CHANGE:
                segments_with_changes += 1
                total_improvement += change.confidence_improvement
        
        # Calculate overall improvement score
        overall_improvement = total_improvement / len(segment_changes) if segment_changes else 0.0
        
        # Generate summary statistics
        summary_stats = self._generate_summary_statistics(segment_changes)
        
        # Calculate quality metrics
        quality_metrics = self._calculate_quality_metrics(original, enhanced, segment_changes)
        
        return ComparisonResult(
            total_segments=len(segment_changes),
            segments_with_changes=segments_with_changes,
            overall_improvement_score=overall_improvement,
            segment_changes=segment_changes,
            summary_statistics=summary_stats,
            quality_metrics=quality_metrics
        )
    
    def calculate_similarity(self, text1: str, text2: str) -> float:
        """Calculate similarity between two text strings."""
        if not text1 and not text2:
            return 1.0
        if not text1 or not text2:
            return 0.0
        
        # Normalize texts
        text1_norm = self._normalize_text(text1)
        text2_norm = self._normalize_text(text2)
        
        if text1_norm == text2_norm:
            return 1.0
        
        # Calculate word-based similarity
        words1 = set(text1_norm.lower().split())
        words2 = set(text2_norm.lower().split())
        
        if not words1 and not words2:
            return 1.0
        if not words1 or not words2:
            return 0.0
        
        intersection = words1.intersection(words2)
        union = words1.union(words2)
        
        word_similarity = len(intersection) / len(union)
        
        # Calculate character-based similarity for fine-grained comparison
        char_similarity = self._calculate_character_similarity(text1_norm, text2_norm)
        
        # Weighted combination
        return (word_similarity * 0.7) + (char_similarity * 0.3)
    
    def identify_changes(self, original_text: str, enhanced_text: str) -> List[str]:
        """Identify specific changes between two text strings."""
        changes = []
        
        if original_text == enhanced_text:
            return changes
        
        # Normalize texts for comparison
        original_norm = self._normalize_text(original_text)
        enhanced_norm = self._normalize_text(enhanced_text)
        
        # Check for filler word removal
        original_fillers = self._count_filler_words(original_norm)
        enhanced_fillers = self._count_filler_words(enhanced_norm)
        if original_fillers > enhanced_fillers:
            changes.append(f"Removed {original_fillers - enhanced_fillers} filler words")
        
        # Check for punctuation improvements
        if self._has_punctuation_improvements(original_norm, enhanced_norm):
            changes.append("Punctuation improved")
        
        # Check for capitalization improvements
        if self._has_capitalization_improvements(original_norm, enhanced_norm):
            changes.append("Capitalization improved")
        
        # Check for technical term corrections
        if self._has_technical_improvements(original_norm, enhanced_norm):
            changes.append("Technical terms corrected")
        
        # Check for word count changes
        original_words = len(original_norm.split())
        enhanced_words = len(enhanced_norm.split())
        if enhanced_words > original_words:
            changes.append(f"Added {enhanced_words - original_words} words")
        elif enhanced_words < original_words:
            changes.append(f"Removed {original_words - enhanced_words} words")
        
        # Check for inaudible section improvements
        if "(inaudible)" in original_norm.lower() and "(inaudible)" not in enhanced_norm.lower():
            changes.append("Inaudible sections transcribed")
        
        return changes
    
    def get_change_statistics(self, comparison_result: ComparisonResult) -> Dict[str, Any]:
        """Get detailed statistics about changes."""
        change_types = {}
        confidence_improvements = []
        word_count_changes = []
        
        for change in comparison_result.segment_changes:
            # Count change types
            change_type = change.change_type.value
            change_types[change_type] = change_types.get(change_type, 0) + 1
            
            # Collect confidence improvements
            if change.confidence_improvement > 0:
                confidence_improvements.append(change.confidence_improvement)
            
            # Collect word count changes
            word_count_changes.append(change.word_count_change)
        
        return {
            "change_type_distribution": change_types,
            "average_confidence_improvement": sum(confidence_improvements) / len(confidence_improvements) if confidence_improvements else 0.0,
            "total_confidence_improvement": sum(confidence_improvements),
            "average_word_count_change": sum(word_count_changes) / len(word_count_changes) if word_count_changes else 0.0,
            "total_word_count_change": sum(word_count_changes),
            "segments_improved": len(confidence_improvements),
            "segments_with_word_changes": len([c for c in word_count_changes if c != 0])
        }
    
    def _compare_segment(
        self, 
        original_segment: Dict[str, Any], 
        enhanced_segment: Dict[str, Any], 
        segment_index: int
    ) -> SegmentChange:
        """Compare a single segment between original and enhanced versions."""
        original_text = original_segment.get("text", "")
        enhanced_text = enhanced_segment.get("text", "")
        
        # Calculate similarity
        similarity = self.calculate_similarity(original_text, enhanced_text)
        
        # Determine change type
        change_type = self._determine_change_type(original_text, enhanced_text, similarity)
        
        # Calculate confidence improvement
        original_confidence = original_segment.get("confidence", 0.8)
        enhanced_confidence = enhanced_segment.get("confidence", 0.9)
        confidence_improvement = enhanced_confidence - original_confidence
        
        # Calculate word count change
        original_words = len(original_text.split())
        enhanced_words = len(enhanced_text.split())
        word_count_change = enhanced_words - original_words
        
        # Identify specific changes
        changes_summary = self.identify_changes(original_text, enhanced_text)
        
        return SegmentChange(
            segment_index=segment_index,
            original_text=original_text,
            enhanced_text=enhanced_text,
            change_type=change_type,
            confidence_improvement=confidence_improvement,
            word_count_change=word_count_change,
            changes_summary=changes_summary,
            start_time=original_segment.get("start", 0.0),
            end_time=original_segment.get("end", 0.0)
        )
    
    def _determine_change_type(self, original_text: str, enhanced_text: str, similarity: float) -> ChangeType:
        """Determine the type of change between two texts."""
        if similarity >= self.high_similarity_threshold:
            if original_text == enhanced_text:
                return ChangeType.NO_CHANGE
            else:
                return ChangeType.FORMATTING
        elif similarity >= self.medium_similarity_threshold:
            # Check if it's a correction
            if self._is_correction(original_text, enhanced_text):
                return ChangeType.CORRECTION
            else:
                return ChangeType.MODIFICATION
        elif similarity >= self.low_similarity_threshold:
            return ChangeType.MODIFICATION
        else:
            # Check if it's an addition or deletion
            if len(enhanced_text) > len(original_text) * 1.5:
                return ChangeType.ADDITION
            elif len(original_text) > len(enhanced_text) * 1.5:
                return ChangeType.DELETION
            else:
                return ChangeType.MODIFICATION
    
    def _normalize_text(self, text: str) -> str:
        """Normalize text for comparison."""
        # Remove extra whitespace
        text = re.sub(r'\s+', ' ', text.strip())
        
        # Normalize quotes and apostrophes - using simpler patterns
        text = re.sub(r'["""]', '"', text)
        text = re.sub(r"[''']", "'", text)
        
        return text
    
    def _calculate_character_similarity(self, text1: str, text2: str) -> float:
        """Calculate character-based similarity using Levenshtein distance approximation."""
        if not text1 and not text2:
            return 1.0
        if not text1 or not text2:
            return 0.0
        
        # Simple character-based similarity
        chars1 = set(text1.lower())
        chars2 = set(text2.lower())
        
        intersection = chars1.intersection(chars2)
        union = chars1.union(chars2)
        
        return len(intersection) / len(union) if union else 0.0
    
    def _count_filler_words(self, text: str) -> int:
        """Count filler words in text."""
        filler_pattern = r'\b(um|uh|er|ah|like|you know|i mean)\b'
        return len(re.findall(filler_pattern, text, re.IGNORECASE))
    
    def _has_punctuation_improvements(self, original: str, enhanced: str) -> bool:
        """Check if punctuation has been improved."""
        # Count sentence endings
        original_endings = len(re.findall(r'[.!?]', original))
        enhanced_endings = len(re.findall(r'[.!?]', enhanced))
        
        # Check for better sentence structure
        return enhanced_endings > original_endings
    
    def _has_capitalization_improvements(self, original: str, enhanced: str) -> bool:
        """Check if capitalization has been improved."""
        # Count proper capitalization
        original_proper = len(re.findall(r'\b[A-Z][a-z]+\b', original))
        enhanced_proper = len(re.findall(r'\b[A-Z][a-z]+\b', enhanced))
        
        return enhanced_proper > original_proper
    
    def _has_technical_improvements(self, original: str, enhanced: str) -> bool:
        """Check if technical terms have been improved."""
        # Count technical patterns
        original_tech = 0
        enhanced_tech = 0
        
        for pattern in self.change_patterns["technical"]:
            original_tech += len(re.findall(pattern, original))
            enhanced_tech += len(re.findall(pattern, enhanced))
        
        return enhanced_tech > original_tech
    
    def _is_correction(self, original: str, enhanced: str) -> bool:
        """Check if the change is a correction."""
        # Check for filler word removal
        original_fillers = self._count_filler_words(original)
        enhanced_fillers = self._count_filler_words(enhanced)
        
        if original_fillers > enhanced_fillers:
            return True
        
        # Check for inaudible section improvements
        if "(inaudible)" in original.lower() and "(inaudible)" not in enhanced.lower():
            return True
        
        # Check for obvious spelling corrections
        if len(original.split()) == len(enhanced.split()):
            # Same word count, might be spelling correction
            return True
        
        return False
    
    def _generate_summary_statistics(self, segment_changes: List[SegmentChange]) -> Dict[str, Any]:
        """Generate summary statistics from segment changes."""
        change_types = {}
        confidence_improvements = []
        word_count_changes = []
        
        for change in segment_changes:
            # Count change types
            change_type = change.change_type.value
            change_types[change_type] = change_types.get(change_type, 0) + 1
            
            # Collect improvements
            if change.confidence_improvement > 0:
                confidence_improvements.append(change.confidence_improvement)
            
            word_count_changes.append(change.word_count_change)
        
        return {
            "change_type_distribution": change_types,
            "total_confidence_improvement": sum(confidence_improvements),
            "average_confidence_improvement": sum(confidence_improvements) / len(confidence_improvements) if confidence_improvements else 0.0,
            "total_word_count_change": sum(word_count_changes),
            "average_word_count_change": sum(word_count_changes) / len(word_count_changes) if word_count_changes else 0.0,
            "segments_improved": len(confidence_improvements)
        }
    
    def _calculate_quality_metrics(
        self, 
        original: List[Dict[str, Any]], 
        enhanced: List[Dict[str, Any]], 
        segment_changes: List[SegmentChange]
    ) -> Dict[str, float]:
        """Calculate quality metrics for the comparison."""
        if not original or not enhanced:
            return {}
        
        # Calculate average confidence improvements
        confidence_improvements = [c.confidence_improvement for c in segment_changes if c.confidence_improvement > 0]
        avg_confidence_improvement = sum(confidence_improvements) / len(confidence_improvements) if confidence_improvements else 0.0
        
        # Calculate text quality improvements
        original_quality = self._calculate_overall_text_quality(original)
        enhanced_quality = self._calculate_overall_text_quality(enhanced)
        quality_improvement = enhanced_quality - original_quality
        
        # Calculate readability improvements
        original_readability = self._calculate_readability(original)
        enhanced_readability = self._calculate_readability(enhanced)
        readability_improvement = enhanced_readability - original_readability
        
        return {
            "average_confidence_improvement": avg_confidence_improvement,
            "text_quality_improvement": quality_improvement,
            "readability_improvement": readability_improvement,
            "overall_enhancement_score": (avg_confidence_improvement + quality_improvement + readability_improvement) / 3
        }
    
    def _calculate_overall_text_quality(self, segments: List[Dict[str, Any]]) -> float:
        """Calculate overall text quality score."""
        if not segments:
            return 0.0
        
        total_quality = 0.0
        total_words = 0
        
        for segment in segments:
            text = segment.get("text", "")
            words = text.split()
            total_words += len(words)
            
            # Simple quality indicators
            quality = 1.0
            
            # Penalize filler words
            filler_count = self._count_filler_words(text)
            if filler_count > 0:
                quality -= (filler_count / len(words)) * 0.3
            
            # Penalize inaudible sections
            if "(inaudible)" in text.lower():
                quality -= 0.2
            
            # Penalize very short segments
            if len(words) < 3:
                quality -= 0.1
            
            total_quality += quality * len(words)
        
        return total_quality / total_words if total_words > 0 else 0.0
    
    def _calculate_readability(self, segments: List[Dict[str, Any]]) -> float:
        """Calculate readability score."""
        if not segments:
            return 0.0
        
        total_text = " ".join(segment.get("text", "") for segment in segments)
        words = total_text.split()
        sentences = re.split(r'[.!?]+', total_text)
        
        if not words or not sentences:
            return 0.0
        
        # Simple Flesch Reading Ease approximation
        avg_sentence_length = len(words) / len(sentences)
        avg_word_length = sum(len(word) for word in words) / len(words)
        
        # Normalize to 0-1 scale
        readability = max(0, 1 - (avg_sentence_length / 20) - (avg_word_length / 10))
        
        return readability
    
    async def _initialize_impl(self) -> None:
        """Initialize the transcript comparer."""
        logger.info("TranscriptComparer initialized")


def create_transcript_comparer() -> TranscriptComparer:
    """Create a transcript comparer instance."""
    return TranscriptComparer()
