#!/usr/bin/env python3
"""
Pattern Confidence Scoring and Retirement System
===============================================

This module implements MIRA's pattern confidence scoring and retirement system,
enabling self-improving intelligence through pattern performance tracking,
automatic retirement of failing patterns, and reinforcement of successful ones.

Key Features:
- Real-time pattern confidence scoring based on success/failure rates
- Automatic pattern retirement when confidence falls below thresholds
- Pattern reinforcement through success rate boosting
- Usage frequency tracking and decay models
- Performance analytics and trend analysis
- Pattern lifecycle management with graceful degradation

Self-Improving Intelligence:
- Patterns that consistently help users get reinforced
- Patterns that consistently fail get deprioritized and eventually retired
- Success patterns spawn similar patterns (pattern breeding)
- Meta-learning from pattern performance trends

Author: MIRA Pattern Intelligence System
Version: 1.0 (Self-Improving Pattern Evolution)
"""

import os
import json
import datetime
import statistics
from typing import Dict, List, Optional, Tuple, Set
from pathlib import Path
from dataclasses import dataclass, asdict
from enum import Enum
import logging

logger = logging.getLogger(__name__)

class PatternLifecycleState(Enum):
    """Lifecycle states for pattern confidence management."""
    ACTIVE = "active"
    PROBATION = "probation"  # Low confidence, under review
    DEPRECATED = "deprecated"  # Scheduled for retirement
    RETIRED = "retired"  # No longer used
    CHAMPION = "champion"  # High-performing pattern

class PatternPerformanceMetric(Enum):
    """Metrics for measuring pattern performance."""
    SUCCESS_RATE = "success_rate"
    USER_SATISFACTION = "user_satisfaction"
    USAGE_FREQUENCY = "usage_frequency"
    CONTEXTUAL_RELEVANCE = "contextual_relevance"
    RESPONSE_QUALITY = "response_quality"

@dataclass
class PatternUsageEvent:
    """Records a single pattern usage event for scoring."""
    pattern_id: str
    timestamp: str
    context: Dict
    success: bool
    user_feedback: Optional[float] = None  # 0.0-1.0 satisfaction score
    response_quality: Optional[float] = None  # 0.0-1.0 quality score
    relevance_score: Optional[float] = None  # 0.0-1.0 contextual relevance
    
    def __post_init__(self):
        if not self.timestamp:
            self.timestamp = datetime.datetime.now().isoformat()

@dataclass
class PatternConfidenceScore:
    """Comprehensive confidence scoring for a pattern."""
    pattern_id: str
    overall_confidence: float
    success_rate: float
    usage_count: int
    total_usage: int
    last_used: str
    lifecycle_state: PatternLifecycleState
    performance_metrics: Dict[PatternPerformanceMetric, float]
    confidence_trend: List[float]  # Historical confidence scores
    retirement_reason: Optional[str] = None
    champion_since: Optional[str] = None
    
    def is_healthy(self) -> bool:
        """Check if pattern is performing well."""
        return (self.overall_confidence >= 0.7 and 
                self.success_rate >= 0.6 and 
                self.usage_count >= 3)
    
    def should_retire(self) -> bool:
        """Check if pattern should be retired."""
        return (self.overall_confidence < 0.3 or 
                (self.success_rate < 0.2 and self.usage_count >= 10) or
                self.lifecycle_state == PatternLifecycleState.RETIRED)
    
    def is_champion(self) -> bool:
        """Check if pattern qualifies as champion."""
        return (self.overall_confidence >= 0.9 and 
                self.success_rate >= 0.8 and 
                self.usage_count >= 20)

class PatternConfidenceSystem:
    """
    Manages pattern confidence scoring, retirement, and reinforcement.
    """
    
    def __init__(self, memory_dir: str):
        self.memory_dir = memory_dir
        self.confidence_dir = os.path.join(memory_dir, "pattern_confidence")
        os.makedirs(self.confidence_dir, exist_ok=True)
        
        # Storage files
        self.usage_events_file = os.path.join(self.confidence_dir, "pattern_usage_events.jsonl")
        self.confidence_scores_file = os.path.join(self.confidence_dir, "pattern_confidence_scores.json")
        self.retired_patterns_file = os.path.join(self.confidence_dir, "retired_patterns.json")
        self.champion_patterns_file = os.path.join(self.confidence_dir, "champion_patterns.json")
        
        # Load existing data
        self.confidence_scores = self._load_confidence_scores()
        self.retired_patterns = self._load_retired_patterns()
        self.champion_patterns = self._load_champion_patterns()
        
        # Configuration
        self.confidence_thresholds = {
            "retirement": 0.3,
            "probation": 0.5,
            "champion": 0.9,
            "min_usage_for_retirement": 10,
            "min_usage_for_champion": 20
        }
        
        # Weights for different performance metrics
        self.metric_weights = {
            PatternPerformanceMetric.SUCCESS_RATE: 0.4,
            PatternPerformanceMetric.USER_SATISFACTION: 0.3,
            PatternPerformanceMetric.USAGE_FREQUENCY: 0.1,
            PatternPerformanceMetric.CONTEXTUAL_RELEVANCE: 0.15,
            PatternPerformanceMetric.RESPONSE_QUALITY: 0.05
        }
    
    def _load_confidence_scores(self) -> Dict[str, PatternConfidenceScore]:
        """Load existing pattern confidence scores."""
        if not os.path.exists(self.confidence_scores_file):
            return {}
        
        try:
            with open(self.confidence_scores_file, 'r') as f:
                data = json.load(f)
                scores = {}
                for pattern_id, score_data in data.items():
                    # Convert enum strings back to enums
                    score_data['lifecycle_state'] = PatternLifecycleState(score_data['lifecycle_state'])
                    
                    # Convert performance metrics
                    metrics = {}
                    for metric_str, value in score_data.get('performance_metrics', {}).items():
                        try:
                            metric = PatternPerformanceMetric(metric_str)
                            metrics[metric] = value
                        except ValueError:
                            continue
                    score_data['performance_metrics'] = metrics
                    
                    scores[pattern_id] = PatternConfidenceScore(**score_data)
                return scores
        except Exception as e:
            logger.debug(f"Failed to load confidence scores: {e}")
            return {}
    
    def _save_confidence_scores(self):
        """Save pattern confidence scores."""
        try:
            data = {}
            for pattern_id, score in self.confidence_scores.items():
                score_data = asdict(score)
                # Convert enums to strings for JSON serialization
                score_data['lifecycle_state'] = score.lifecycle_state.value
                
                # Convert performance metrics enum keys to strings
                metrics = {}
                for metric, value in score.performance_metrics.items():
                    metrics[metric.value] = value
                score_data['performance_metrics'] = metrics
                
                data[pattern_id] = score_data
            
            with open(self.confidence_scores_file, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save confidence scores: {e}")
    
    def _load_retired_patterns(self) -> Dict[str, Dict]:
        """Load retired patterns metadata."""
        if not os.path.exists(self.retired_patterns_file):
            return {}
        
        try:
            with open(self.retired_patterns_file, 'r') as f:
                return json.load(f)
        except Exception as e:
            logger.debug(f"Failed to load retired patterns: {e}")
            return {}
    
    def _save_retired_patterns(self):
        """Save retired patterns metadata."""
        try:
            with open(self.retired_patterns_file, 'w') as f:
                json.dump(self.retired_patterns, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save retired patterns: {e}")
    
    def _load_champion_patterns(self) -> Dict[str, Dict]:
        """Load champion patterns metadata."""
        if not os.path.exists(self.champion_patterns_file):
            return {}
        
        try:
            with open(self.champion_patterns_file, 'r') as f:
                return json.load(f)
        except Exception as e:
            logger.debug(f"Failed to load champion patterns: {e}")
            return {}
    
    def _save_champion_patterns(self):
        """Save champion patterns metadata."""
        try:
            with open(self.champion_patterns_file, 'w') as f:
                json.dump(self.champion_patterns, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save champion patterns: {e}")
    
    def record_pattern_usage(self, usage_event: PatternUsageEvent):
        """Record a pattern usage event for scoring."""
        try:
            # Save usage event
            with open(self.usage_events_file, 'a') as f:
                f.write(json.dumps(asdict(usage_event)) + '\n')
            
            # Update pattern confidence score
            self._update_pattern_confidence(usage_event)
            
            # Check for lifecycle state changes
            self._evaluate_pattern_lifecycle(usage_event.pattern_id)
            
            logger.debug(f"🎯 CONFIDENCE: Recorded usage for pattern {usage_event.pattern_id}, success={usage_event.success}")
            
        except Exception as e:
            logger.error(f"Failed to record pattern usage: {e}")
    
    def _update_pattern_confidence(self, usage_event: PatternUsageEvent):
        """Update confidence score for a pattern based on usage event."""
        pattern_id = usage_event.pattern_id
        
        # Get or create confidence score
        if pattern_id not in self.confidence_scores:
            self.confidence_scores[pattern_id] = PatternConfidenceScore(
                pattern_id=pattern_id,
                overall_confidence=0.5,  # Start neutral
                success_rate=0.0,
                usage_count=0,
                total_usage=0,
                last_used="",
                lifecycle_state=PatternLifecycleState.ACTIVE,
                performance_metrics={},
                confidence_trend=[]
            )
        
        score = self.confidence_scores[pattern_id]
        
        # Update usage statistics
        score.total_usage += 1
        if usage_event.success:
            score.usage_count += 1
        
        score.last_used = usage_event.timestamp
        
        # Calculate success rate
        score.success_rate = score.usage_count / score.total_usage if score.total_usage > 0 else 0.0
        
        # Update performance metrics
        self._update_performance_metrics(score, usage_event)
        
        # Calculate overall confidence
        score.overall_confidence = self._calculate_overall_confidence(score)
        
        # Update confidence trend (keep last 20 values)
        score.confidence_trend.append(score.overall_confidence)
        if len(score.confidence_trend) > 20:
            score.confidence_trend = score.confidence_trend[-20:]
        
        # Save updated scores
        self._save_confidence_scores()
    
    def _update_performance_metrics(self, score: PatternConfidenceScore, usage_event: PatternUsageEvent):
        """Update detailed performance metrics for a pattern."""
        metrics = score.performance_metrics
        
        # Success rate metric
        metrics[PatternPerformanceMetric.SUCCESS_RATE] = score.success_rate
        
        # User satisfaction (if provided)
        if usage_event.user_feedback is not None:
            current_satisfaction = metrics.get(PatternPerformanceMetric.USER_SATISFACTION, 0.5)
            # Exponential moving average
            metrics[PatternPerformanceMetric.USER_SATISFACTION] = (
                0.7 * current_satisfaction + 0.3 * usage_event.user_feedback
            )
        
        # Response quality (if provided)
        if usage_event.response_quality is not None:
            current_quality = metrics.get(PatternPerformanceMetric.RESPONSE_QUALITY, 0.5)
            metrics[PatternPerformanceMetric.RESPONSE_QUALITY] = (
                0.7 * current_quality + 0.3 * usage_event.response_quality
            )
        
        # Contextual relevance (if provided)
        if usage_event.relevance_score is not None:
            current_relevance = metrics.get(PatternPerformanceMetric.CONTEXTUAL_RELEVANCE, 0.5)
            metrics[PatternPerformanceMetric.CONTEXTUAL_RELEVANCE] = (
                0.7 * current_relevance + 0.3 * usage_event.relevance_score
            )
        
        # Usage frequency (based on recent usage)
        recent_usage_score = min(1.0, score.total_usage / 100.0)  # Scale to 0-1
        metrics[PatternPerformanceMetric.USAGE_FREQUENCY] = recent_usage_score
    
    def _calculate_overall_confidence(self, score: PatternConfidenceScore) -> float:
        """Calculate overall confidence based on weighted performance metrics."""
        if not score.performance_metrics:
            return score.success_rate
        
        weighted_sum = 0.0
        total_weight = 0.0
        
        for metric, weight in self.metric_weights.items():
            if metric in score.performance_metrics:
                weighted_sum += score.performance_metrics[metric] * weight
                total_weight += weight
        
        if total_weight == 0:
            return score.success_rate
        
        # Normalize by actual weights used
        base_confidence = weighted_sum / total_weight
        
        # Apply usage count bonus/penalty
        usage_factor = 1.0
        if score.total_usage < 5:
            usage_factor = 0.8  # Reduce confidence for low usage
        elif score.total_usage > 50:
            usage_factor = 1.1  # Boost confidence for high usage
        
        # Apply trend bonus/penalty
        trend_factor = 1.0
        if len(score.confidence_trend) >= 5:
            recent_trend = score.confidence_trend[-5:]
            if len(recent_trend) > 1:
                trend_slope = (recent_trend[-1] - recent_trend[0]) / len(recent_trend)
                trend_factor = 1.0 + (trend_slope * 0.2)  # Small trend adjustment
        
        final_confidence = base_confidence * usage_factor * trend_factor
        return max(0.0, min(1.0, final_confidence))  # Clamp to 0-1
    
    def _evaluate_pattern_lifecycle(self, pattern_id: str):
        """Evaluate and update pattern lifecycle state."""
        if pattern_id not in self.confidence_scores:
            return
        
        score = self.confidence_scores[pattern_id]
        old_state = score.lifecycle_state
        
        # Check for retirement
        if score.should_retire() and score.lifecycle_state != PatternLifecycleState.RETIRED:
            self._retire_pattern(pattern_id, score)
            return
        
        # Check for champion status
        if score.is_champion() and score.lifecycle_state != PatternLifecycleState.CHAMPION:
            self._promote_to_champion(pattern_id, score)
            return
        
        # Check for probation
        if (score.overall_confidence < self.confidence_thresholds["probation"] and 
            score.total_usage >= 5 and 
            score.lifecycle_state == PatternLifecycleState.ACTIVE):
            score.lifecycle_state = PatternLifecycleState.PROBATION
            logger.info(f"📉 PATTERN PROBATION: {pattern_id} moved to probation (confidence: {score.overall_confidence:.2f})")
        
        # Check for return to active from probation
        elif (score.overall_confidence >= self.confidence_thresholds["probation"] and 
              score.lifecycle_state == PatternLifecycleState.PROBATION):
            score.lifecycle_state = PatternLifecycleState.ACTIVE
            logger.info(f"📈 PATTERN RECOVERY: {pattern_id} returned to active status")
        
        # Save if state changed
        if old_state != score.lifecycle_state:
            self._save_confidence_scores()
    
    def _retire_pattern(self, pattern_id: str, score: PatternConfidenceScore):
        """Retire a poorly performing pattern."""
        reason = "Low confidence"
        if score.success_rate < 0.2 and score.total_usage >= 10:
            reason = "Consistently poor success rate"
        elif score.overall_confidence < 0.3:
            reason = "Overall low confidence score"
        
        score.lifecycle_state = PatternLifecycleState.RETIRED
        score.retirement_reason = reason
        
        # Add to retired patterns registry
        self.retired_patterns[pattern_id] = {
            "retired_at": datetime.datetime.now().isoformat(),
            "reason": reason,
            "final_confidence": score.overall_confidence,
            "final_success_rate": score.success_rate,
            "total_usage": score.total_usage
        }
        
        self._save_confidence_scores()
        self._save_retired_patterns()
        
        logger.info(f"🚮 PATTERN RETIREMENT: {pattern_id} retired due to {reason} (confidence: {score.overall_confidence:.2f}, success: {score.success_rate:.2f})")
    
    def _promote_to_champion(self, pattern_id: str, score: PatternConfidenceScore):
        """Promote a high-performing pattern to champion status."""
        score.lifecycle_state = PatternLifecycleState.CHAMPION
        score.champion_since = datetime.datetime.now().isoformat()
        
        # Add to champion patterns registry
        self.champion_patterns[pattern_id] = {
            "promoted_at": score.champion_since,
            "confidence": score.overall_confidence,
            "success_rate": score.success_rate,
            "total_usage": score.total_usage,
            "performance_metrics": {k.value: v for k, v in score.performance_metrics.items()}
        }
        
        self._save_confidence_scores()
        self._save_champion_patterns()
        
        logger.info(f"🏆 PATTERN CHAMPION: {pattern_id} promoted to champion (confidence: {score.overall_confidence:.2f}, success: {score.success_rate:.2f})")
    
    def get_active_patterns(self, min_confidence: float = 0.5) -> List[Tuple[str, PatternConfidenceScore]]:
        """Get all active patterns above minimum confidence threshold."""
        active_patterns = []
        for pattern_id, score in self.confidence_scores.items():
            if (score.lifecycle_state in [PatternLifecycleState.ACTIVE, PatternLifecycleState.CHAMPION] and
                score.overall_confidence >= min_confidence):
                active_patterns.append((pattern_id, score))
        
        # Sort by confidence (highest first)
        return sorted(active_patterns, key=lambda x: x[1].overall_confidence, reverse=True)
    
    def get_champion_patterns(self) -> List[Tuple[str, PatternConfidenceScore]]:
        """Get all champion patterns."""
        champions = []
        for pattern_id, score in self.confidence_scores.items():
            if score.lifecycle_state == PatternLifecycleState.CHAMPION:
                champions.append((pattern_id, score))
        
        return sorted(champions, key=lambda x: x[1].overall_confidence, reverse=True)
    
    def get_patterns_needing_attention(self) -> Dict[str, List[Tuple[str, PatternConfidenceScore]]]:
        """Get patterns that need attention (probation, low confidence, etc.)."""
        attention_patterns = {
            "probation": [],
            "low_confidence": [],
            "declining_trend": [],
            "unused": []
        }
        
        for pattern_id, score in self.confidence_scores.items():
            if score.lifecycle_state == PatternLifecycleState.PROBATION:
                attention_patterns["probation"].append((pattern_id, score))
            elif score.overall_confidence < 0.5 and score.lifecycle_state == PatternLifecycleState.ACTIVE:
                attention_patterns["low_confidence"].append((pattern_id, score))
            
            # Check for declining trend
            if len(score.confidence_trend) >= 5:
                recent_trend = score.confidence_trend[-5:]
                if recent_trend[0] - recent_trend[-1] > 0.2:  # Significant decline
                    attention_patterns["declining_trend"].append((pattern_id, score))
            
            # Check for unused patterns
            if score.total_usage == 0:
                attention_patterns["unused"].append((pattern_id, score))
        
        return attention_patterns
    
    def get_confidence_statistics(self) -> Dict:
        """Get overall confidence system statistics."""
        if not self.confidence_scores:
            return {
                "total_patterns": 0,
                "active_patterns": 0,
                "champion_patterns": 0,
                "retired_patterns": 0,
                "average_confidence": 0.0,
                "patterns_by_state": {}
            }
        
        state_counts = {}
        confidence_values = []
        
        for score in self.confidence_scores.values():
            state = score.lifecycle_state
            state_counts[state.value] = state_counts.get(state.value, 0) + 1
            confidence_values.append(score.overall_confidence)
        
        return {
            "total_patterns": len(self.confidence_scores),
            "active_patterns": state_counts.get("active", 0),
            "champion_patterns": state_counts.get("champion", 0),
            "probation_patterns": state_counts.get("probation", 0),
            "retired_patterns": state_counts.get("retired", 0),
            "average_confidence": statistics.mean(confidence_values) if confidence_values else 0.0,
            "median_confidence": statistics.median(confidence_values) if confidence_values else 0.0,
            "patterns_by_state": state_counts,
            "confidence_distribution": {
                "high": len([c for c in confidence_values if c >= 0.8]),
                "medium": len([c for c in confidence_values if 0.5 <= c < 0.8]),
                "low": len([c for c in confidence_values if c < 0.5])
            }
        }
    
    def simulate_pattern_feedback(self, pattern_id: str, success: bool, 
                                user_satisfaction: Optional[float] = None,
                                relevance: Optional[float] = None):
        """Simulate pattern feedback for testing purposes."""
        usage_event = PatternUsageEvent(
            pattern_id=pattern_id,
            timestamp=datetime.datetime.now().isoformat(),
            context={"source": "simulation"},
            success=success,
            user_feedback=user_satisfaction,
            relevance_score=relevance
        )
        
        self.record_pattern_usage(usage_event)


def get_pattern_confidence_system(memory_dir: str) -> PatternConfidenceSystem:
    """Get the pattern confidence system instance."""
    return PatternConfidenceSystem(memory_dir)


# Example usage and testing
if __name__ == "__main__":
    import tempfile
    
    # Test the pattern confidence system
    with tempfile.TemporaryDirectory() as temp_dir:
        confidence_system = PatternConfidenceSystem(temp_dir)
        
        # Simulate some pattern usage
        patterns = ["test_pattern_1", "test_pattern_2", "test_pattern_3"]
        
        print("🧪 Testing Pattern Confidence System")
        
        # Pattern 1: High success rate
        for i in range(15):
            success = i < 12  # 80% success rate
            confidence_system.simulate_pattern_feedback(
                patterns[0], success, 
                user_satisfaction=0.8 if success else 0.3,
                relevance=0.9 if success else 0.4
            )
        
        # Pattern 2: Low success rate
        for i in range(15):
            success = i < 3  # 20% success rate
            confidence_system.simulate_pattern_feedback(
                patterns[1], success,
                user_satisfaction=0.2 if not success else 0.7,
                relevance=0.3 if not success else 0.8
            )
        
        # Pattern 3: Champion pattern
        for i in range(25):
            success = i < 23  # 92% success rate
            confidence_system.simulate_pattern_feedback(
                patterns[2], success,
                user_satisfaction=0.9 if success else 0.4,
                relevance=0.95 if success else 0.5
            )
        
        # Get results
        stats = confidence_system.get_confidence_statistics()
        print(f"📊 Total patterns: {stats['total_patterns']}")
        print(f"🏆 Champion patterns: {stats['champion_patterns']}")
        print(f"🚮 Retired patterns: {stats['retired_patterns']}")
        print(f"📈 Average confidence: {stats['average_confidence']:.2f}")
        
        champions = confidence_system.get_champion_patterns()
        if champions:
            print(f"\n🏆 Champion patterns:")
            for pattern_id, score in champions:
                print(f"  - {pattern_id}: {score.overall_confidence:.2f} confidence, {score.success_rate:.2f} success rate")
        
        attention = confidence_system.get_patterns_needing_attention()
        for category, patterns_list in attention.items():
            if patterns_list:
                print(f"\n⚠️ {category.title()} patterns:")
                for pattern_id, score in patterns_list:
                    print(f"  - {pattern_id}: {score.overall_confidence:.2f} confidence")