"""
Sonnet Content Classifier for High-Volume Blog Analysis

Uses Claude Sonnet 3.5 for cost-efficient classification of 2000+ content items,
extracting technical topics, difficulty levels, brand mentions, and semantic concepts.
"""

import os
import json
import logging
import asyncio
import re
from typing import Dict, List, Optional, Any, Tuple
from dataclasses import dataclass, asdict
from pathlib import Path
import anthropic
from anthropic import AsyncAnthropic
from datetime import datetime
from collections import defaultdict, Counter

logger = logging.getLogger(__name__)

@dataclass
class ContentClassification:
    """Classification result for a single content item"""
    content_id: str
    title: str
    source: str
    
    # Technical classification
    primary_topics: List[str]  # Main technical topics (specific)
    secondary_topics: List[str]  # Supporting topics
    technical_depth: str  # beginner/intermediate/advanced/expert
    
    # Content characteristics
    content_type: str  # tutorial/troubleshooting/theory/product/news
    content_format: str  # video/article/social_post
    
    # Brand and product intelligence
    brands_mentioned: List[str]
    products_mentioned: List[str]
    tools_mentioned: List[str]
    
    # Semantic analysis
    semantic_keywords: List[str]  # Extracted concepts not in predefined lists
    related_concepts: List[str]  # Conceptually related topics
    
    # Audience and engagement
    target_audience: str  # DIY/professional/commercial/residential
    engagement_potential: float  # 0-1 score
    
    # Blog relevance
    blog_worthiness: float  # 0-1 score for blog content potential
    suggested_blog_angle: Optional[str]  # How to approach this topic for blog

@dataclass
class BatchClassificationResult:
    """Result of batch classification"""
    classifications: List[ContentClassification]
    processing_time: float
    tokens_used: int
    cost_estimate: float
    errors: List[Dict[str, Any]]

class SonnetContentClassifier:
    """
    High-volume content classification using Claude Sonnet 3.5
    Optimized for batch processing and cost efficiency
    """
    
    # Sonnet pricing (as of 2024)
    INPUT_TOKEN_COST = 0.003 / 1000  # $3 per million input tokens
    OUTPUT_TOKEN_COST = 0.015 / 1000  # $15 per million output tokens
    
    def __init__(self, api_key: Optional[str] = None, dry_run: bool = False):
        """Initialize Sonnet classifier with API credentials"""
        self.api_key = api_key or os.getenv('ANTHROPIC_API_KEY')
        self.dry_run = dry_run
        
        if not self.dry_run and not self.api_key:
            raise ValueError("ANTHROPIC_API_KEY required for Sonnet classifier")
            
        self.client = AsyncAnthropic(api_key=self.api_key) if not dry_run else None
        self.model = "claude-3-5-sonnet-20241022"
        self.batch_size = 10  # Process 10 items per API call
        self.max_tokens_per_item = 200  # Tight limit for cost control
        
        # Expanded technical categories for HVAC
        self.technical_categories = {
            'refrigeration': ['compressor', 'evaporator', 'condenser', 'refrigerant', 'subcooling', 'superheat', 'txv', 'metering', 'recovery'],
            'electrical': ['capacitor', 'contactor', 'relay', 'transformer', 'voltage', 'amperage', 'multimeter', 'ohm', 'circuit'],
            'controls': ['thermostat', 'sensor', 'bms', 'automation', 'programming', 'sequence', 'pid', 'setpoint'],
            'airflow': ['cfm', 'static pressure', 'ductwork', 'blower', 'fan', 'filter', 'grille', 'damper'],
            'heating': ['furnace', 'boiler', 'heat pump', 'burner', 'heat exchanger', 'combustion', 'venting'],
            'cooling': ['air conditioning', 'chiller', 'cooling tower', 'dx system', 'split system'],
            'installation': ['brazing', 'piping', 'mounting', 'commissioning', 'startup', 'evacuation'],
            'diagnostics': ['troubleshooting', 'testing', 'measurement', 'leak detection', 'performance'],
            'maintenance': ['cleaning', 'filter change', 'coil cleaning', 'preventive', 'inspection'],
            'efficiency': ['seer', 'eer', 'cop', 'energy savings', 'optimization', 'load calculation'],
            'safety': ['lockout tagout', 'ppe', 'refrigerant handling', 'electrical safety', 'osha'],
            'codes': ['ashrae', 'nec', 'imc', 'epa', 'building code', 'permit', 'compliance'],
            'commercial': ['vrf', 'vav', 'rooftop unit', 'package unit', 'cooling tower', 'chiller'],
            'residential': ['mini split', 'window unit', 'central air', 'ductless', 'zoning'],
            'tools': ['manifold', 'vacuum pump', 'recovery machine', 'leak detector', 'thermometer']
        }
        
        # Brand tracking
        self.known_brands = [
            'carrier', 'trane', 'lennox', 'goodman', 'rheem', 'york', 'daikin',
            'mitsubishi', 'fujitsu', 'copeland', 'danfoss', 'honeywell', 'emerson',
            'johnson controls', 'siemens', 'white rogers', 'sporlan', 'parker',
            'yellow jacket', 'fieldpiece', 'fluke', 'testo', 'bacharach', 'amrad'
        ]
        
        # Initialize cost tracking
        self.total_tokens_used = 0
        self.total_cost = 0.0
        
    async def classify_batch(self, content_items: List[Dict]) -> BatchClassificationResult:
        """
        Classify a batch of content items with Sonnet
        
        Args:
            content_items: List of content dictionaries with 'title', 'description', 'id', 'source'
            
        Returns:
            BatchClassificationResult with classifications and metrics
        """
        start_time = datetime.now()
        classifications = []
        errors = []
        
        # Prepare batch prompt
        prompt = self._create_batch_prompt(content_items)
        
        try:
            # Call Sonnet API
            response = await self.client.messages.create(
                model=self.model,
                max_tokens=self.max_tokens_per_item * len(content_items),
                temperature=0.3,  # Lower temperature for consistent classification
                messages=[
                    {
                        "role": "user",
                        "content": prompt
                    }
                ]
            )
            
            # Parse response
            classifications = self._parse_batch_response(response.content[0].text, content_items)
            
            # Track token usage
            tokens_used = response.usage.input_tokens + response.usage.output_tokens
            self.total_tokens_used += tokens_used
            
            # Calculate cost
            cost = (response.usage.input_tokens * self.INPUT_TOKEN_COST + 
                   response.usage.output_tokens * self.OUTPUT_TOKEN_COST)
            self.total_cost += cost
            
        except Exception as e:
            logger.error(f"Error in batch classification: {e}")
            errors.append({
                'error': str(e),
                'batch_size': len(content_items),
                'timestamp': datetime.now().isoformat()
            })
            tokens_used = 0
            cost = 0
            
        processing_time = (datetime.now() - start_time).total_seconds()
        
        return BatchClassificationResult(
            classifications=classifications,
            processing_time=processing_time,
            tokens_used=tokens_used,
            cost_estimate=cost,
            errors=errors
        )
        
    def _create_batch_prompt(self, content_items: List[Dict]) -> str:
        """Create optimized prompt for batch classification"""
        
        # Format content items for analysis
        items_text = ""
        for i, item in enumerate(content_items, 1):
            items_text += f"\n[ITEM {i}]\n"
            items_text += f"Title: {item.get('title', 'N/A')}\n"
            items_text += f"Description: {item.get('description', '')[:500]}\n"  # Limit description length
            if 'categories' in item:
                items_text += f"Tags: {', '.join(item['categories'][:20])}\n"
        
        prompt = f"""Analyze these HVAC content items and classify each one. Be specific and thorough.

{items_text}

For EACH item, extract:
1. Primary topics (be very specific - e.g., "capacitor testing" not just "electrical", "VRF system commissioning" not just "installation")
2. Technical depth: beginner/intermediate/advanced/expert
3. Content type: tutorial/troubleshooting/theory/product_review/news/case_study
4. Brand mentions (any HVAC brands mentioned)
5. Product mentions (specific products or model numbers)
6. Tool mentions (diagnostic tools, equipment)
7. Target audience: DIY_homeowner/professional_tech/commercial_contractor/facility_manager
8. Semantic concepts (technical concepts not explicitly stated but implied)
9. Blog potential (0-1 score) - how suitable for a technical blog post
10. Suggested blog angle (if blog potential > 0.5)

Known HVAC brands to look for: {', '.join(self.known_brands[:20])}

Return a JSON array with one object per item. Keep responses concise but complete.
Format:
[
  {{
    "item_number": 1,
    "primary_topics": ["specific topic 1", "specific topic 2"],
    "technical_depth": "intermediate",
    "content_type": "tutorial",
    "brands": ["brand1"],
    "products": ["model xyz"],
    "tools": ["multimeter", "manifold gauge"],
    "audience": "professional_tech",
    "semantic_concepts": ["heat transfer", "psychrometrics"],
    "blog_potential": 0.8,
    "blog_angle": "Step-by-step guide with common mistakes to avoid"
  }}
]"""
        
        return prompt
        
    def _parse_batch_response(self, response_text: str, original_items: List[Dict]) -> List[ContentClassification]:
        """Parse Sonnet's response into ContentClassification objects"""
        classifications = []
        
        try:
            # Extract JSON from response
            json_match = re.search(r'\[.*\]', response_text, re.DOTALL)
            if json_match:
                response_data = json.loads(json_match.group())
            else:
                # Try to parse the entire response as JSON
                response_data = json.loads(response_text)
                
            for item_data in response_data:
                item_num = item_data.get('item_number', 1) - 1
                if item_num < len(original_items):
                    original = original_items[item_num]
                    
                    classification = ContentClassification(
                        content_id=original.get('id', ''),
                        title=original.get('title', ''),
                        source=original.get('source', ''),
                        primary_topics=item_data.get('primary_topics', []),
                        secondary_topics=item_data.get('semantic_concepts', []),
                        technical_depth=item_data.get('technical_depth', 'intermediate'),
                        content_type=item_data.get('content_type', 'unknown'),
                        content_format=original.get('type', 'unknown'),
                        brands_mentioned=item_data.get('brands', []),
                        products_mentioned=item_data.get('products', []),
                        tools_mentioned=item_data.get('tools', []),
                        semantic_keywords=item_data.get('semantic_concepts', []),
                        related_concepts=[],  # Would need additional processing
                        target_audience=item_data.get('audience', 'professional_tech'),
                        engagement_potential=0.5,  # Would need engagement data
                        blog_worthiness=item_data.get('blog_potential', 0.5),
                        suggested_blog_angle=item_data.get('blog_angle')
                    )
                    classifications.append(classification)
                    
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse JSON response: {e}")
            logger.debug(f"Response text: {response_text[:500]}")
            
        return classifications
        
    async def classify_all_content(self, 
                                  content_items: List[Dict],
                                  progress_callback: Optional[callable] = None) -> Dict[str, Any]:
        """
        Classify all content items in batches
        
        Args:
            content_items: All content items to classify
            progress_callback: Optional callback for progress updates
            
        Returns:
            Dictionary with all classifications and statistics
        """
        all_classifications = []
        total_errors = []
        
        # Process in batches
        for i in range(0, len(content_items), self.batch_size):
            batch = content_items[i:i + self.batch_size]
            
            # Classify batch
            result = await self.classify_batch(batch)
            all_classifications.extend(result.classifications)
            total_errors.extend(result.errors)
            
            # Progress callback
            if progress_callback:
                progress = (i + len(batch)) / len(content_items) * 100
                progress_callback(f"Classified {i + len(batch)}/{len(content_items)} items ({progress:.1f}%)")
                
            # Rate limiting - avoid hitting API limits
            await asyncio.sleep(1)  # 1 second between batches
            
        # Aggregate statistics
        topic_frequency = self._calculate_topic_frequency(all_classifications)
        brand_frequency = self._calculate_brand_frequency(all_classifications)
        
        return {
            'classifications': all_classifications,
            'statistics': {
                'total_items': len(content_items),
                'successfully_classified': len(all_classifications),
                'errors': len(total_errors),
                'total_tokens': self.total_tokens_used,
                'total_cost': self.total_cost,
                'topic_frequency': topic_frequency,
                'brand_frequency': brand_frequency,
                'technical_depth_distribution': self._calculate_depth_distribution(all_classifications)
            },
            'errors': total_errors
        }
        
    def _calculate_topic_frequency(self, classifications: List[ContentClassification]) -> Dict[str, int]:
        """Calculate frequency of topics across all classifications"""
        topic_counter = Counter()
        
        for classification in classifications:
            for topic in classification.primary_topics:
                topic_counter[topic] += 1
            for topic in classification.secondary_topics:
                topic_counter[topic] += 0.5  # Weight secondary topics lower
                
        return dict(topic_counter.most_common(50))
        
    def _calculate_brand_frequency(self, classifications: List[ContentClassification]) -> Dict[str, int]:
        """Calculate frequency of brand mentions"""
        brand_counter = Counter()
        
        for classification in classifications:
            for brand in classification.brands_mentioned:
                brand_counter[brand.lower()] += 1
                
        return dict(brand_counter.most_common(20))
        
    def _calculate_depth_distribution(self, classifications: List[ContentClassification]) -> Dict[str, int]:
        """Calculate distribution of technical depth levels"""
        depth_counter = Counter()
        
        for classification in classifications:
            depth_counter[classification.technical_depth] += 1
            
        return dict(depth_counter)
        
    def export_classifications(self, classifications: List[ContentClassification], output_path: Path):
        """Export classifications to JSON for further analysis"""
        export_data = {
            'metadata': {
                'classifier': 'SonnetContentClassifier',
                'model': self.model,
                'timestamp': datetime.now().isoformat(),
                'total_items': len(classifications)
            },
            'classifications': [asdict(c) for c in classifications]
        }
        
        output_path.write_text(json.dumps(export_data, indent=2))
        logger.info(f"Exported {len(classifications)} classifications to {output_path}")