"""
HVAC Content Classifier using Claude Haiku

Classifies and summarizes HVAC content items into structured JSON format.
"""

import json
import asyncio
import re
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, asdict
from pathlib import Path
import logging
from datetime import datetime
import aiohttp
import os

from .content_parser import ContentItem

logger = logging.getLogger(__name__)


@dataclass 
class ClassifiedContent:
    """Structured classification result for a content item."""
    
    # Original metadata
    id: str
    url: Optional[str]
    date_published: Optional[str]
    author: Optional[str]
    source: str
    
    # Analyzed content
    word_count: Optional[int]
    summary_1_3_sentences: str
    key_learnings: List[str]  # 3-10 bullet points
    content_type: str  # business, technical, educational, marketing, etc.
    application: List[str]  # Residential, Commercial, Industrial, etc.
    categories: List[str]  # Original categories plus inferred ones
    brands_mentioned: List[str]
    tools_mentioned: List[str] 
    topics: List[str]  # Technical topics covered
    meta: Dict[str, Any]  # Catch-all for other important info
    
    # Processing metadata
    classified_at: str
    classification_confidence: float
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)


class ContentClassifier:
    """Classifies HVAC content using Claude Haiku."""
    
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv('ANTHROPIC_API_KEY')
        if not self.api_key:
            raise ValueError("ANTHROPIC_API_KEY environment variable required")
            
        self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
        self.base_url = "https://api.anthropic.com/v1/messages"
        
        # Reasonable rate limiting for 80,000 output tokens/minute limit
        # Each request uses ~1000 tokens, so max 80 requests/minute = ~1.3/second
        self.rate_limit_delay = 1.0  # 1 second between requests (safer than 0.75s)
        self.max_retries = 5  # Standard retry attempts
        self.backoff_factor = 2.0  # Standard backoff multiplier
    
    async def classify_content_item(self, item: ContentItem) -> Optional[ClassifiedContent]:
        """Classify a single content item using Claude Haiku."""
        try:
            # Prepare content for classification
            content_text = self._prepare_content_for_classification(item)
            
            # Create classification prompt
            prompt = self._create_classification_prompt(content_text, item)
            
            # Get classification from Claude Haiku
            classification_result = await self._call_claude_haiku(prompt)
            
            if classification_result:
                # Parse and structure the result
                classified = self._parse_classification_result(
                    classification_result, item
                )
                return classified
                
        except Exception as e:
            self.logger.error(f"Error classifying item {item.id}: {e}")
            
        return None
    
    def _prepare_content_for_classification(self, item: ContentItem) -> str:
        """Prepare content text for classification, focusing on key elements."""
        parts = []
        
        # Title and basic info
        if item.title:
            parts.append(f"TITLE: {item.title}")
        if item.author:
            parts.append(f"AUTHOR: {item.author}")
        if item.content_type:
            parts.append(f"TYPE: {item.content_type}")
        if item.categories:
            parts.append(f"CATEGORIES: {', '.join(item.categories[:5])}")  # Limit categories
        
        # Main content
        if item.description:
            # Limit description length for API efficiency
            description = item.description[:2000] if len(item.description) > 2000 else item.description
            parts.append(f"CONTENT: {description}")
        
        return "\n\n".join(parts)
    
    def _create_classification_prompt(self, content_text: str, item: ContentItem) -> str:
        """Create the classification prompt for Claude Haiku."""
        
        return f"""You are an HVAC industry expert analyzing content for HVAC Know It All. Analyze this content and return ONLY a valid JSON object with the following structure:

{{
    "summary_1_3_sentences": "1-3 sentence summary of the main points",
    "key_learnings": ["3-10 key takeaways as bullet points"],
    "content_type": "one of: technical, business, educational, marketing, news, troubleshooting, installation, maintenance",
    "application": ["one or more of: Residential, Commercial, Industrial, Automotive, Marine"],
    "categories": ["inferred technical categories beyond original tags"],
    "brands_mentioned": ["any HVAC brands, manufacturers, or tools mentioned"],
    "tools_mentioned": ["specific HVAC tools, equipment, or software mentioned"],
    "topics": ["specific technical topics like refrigeration, heat pumps, ductwork, etc."],
    "meta": {{
        "difficulty_level": "beginner/intermediate/advanced",
        "target_audience": "homeowner/technician/contractor/engineer",
        "actionable": true/false,
        "troubleshooting_focus": true/false
    }},
    "confidence": 0.95
}}

CONTENT TO ANALYZE:
{content_text}

Return only the JSON object, no other text."""
    
    async def _call_claude_haiku(self, prompt: str) -> Optional[Dict[str, Any]]:
        """Call Claude Haiku API for classification."""
        headers = {
            "Content-Type": "application/json",
            "x-api-key": self.api_key,
            "anthropic-version": "2023-06-01"
        }
        
        payload = {
            "model": "claude-3-haiku-20240307",
            "max_tokens": 1000,
            "temperature": 0.1,  # Low temperature for consistent classification
            "messages": [
                {
                    "role": "user", 
                    "content": prompt
                }
            ]
        }
        
        async with aiohttp.ClientSession() as session:
            try:
                async with session.post(self.base_url, 
                                      headers=headers, 
                                      json=payload) as response:
                    
                    if response.status == 200:
                        result = await response.json()
                        
                        if 'content' in result and result['content']:
                            content_text = result['content'][0]['text']
                            
                            # Try to parse as JSON
                            try:
                                return json.loads(content_text)
                            except json.JSONDecodeError:
                                # Try to extract JSON from response
                                json_match = re.search(r'\{.*\}', content_text, re.DOTALL)
                                if json_match:
                                    return json.loads(json_match.group(0))
                        else:
                            self.logger.warning("No content in Claude response")
                        
                    elif response.status == 429:
                        # Rate limit hit - return None to trigger retry at higher level
                        error_text = await response.text()
                        self.logger.error(f"Claude API rate limit error 429: {error_text}")
                        return None
                        
                    else:
                        error_text = await response.text()
                        self.logger.error(f"Claude API error {response.status}: {error_text}")
                        
            except Exception as e:
                self.logger.error(f"Error calling Claude API: {e}")
                
        # Apply rate limiting
        await asyncio.sleep(self.rate_limit_delay)
        return None
    
    def _parse_classification_result(self, result: Dict[str, Any], item: ContentItem) -> ClassifiedContent:
        """Parse Claude's classification result into structured format."""
        
        return ClassifiedContent(
            # Original metadata
            id=item.id,
            url=item.url,
            date_published=item.publish_date,
            author=item.author,
            source=item.source,
            
            # Analyzed content  
            word_count=item.word_count,
            summary_1_3_sentences=result.get('summary_1_3_sentences', ''),
            key_learnings=result.get('key_learnings', []),
            content_type=result.get('content_type', 'unknown'),
            application=result.get('application', []),
            categories=list(set(item.categories + result.get('categories', []))),  # Merge and dedupe
            brands_mentioned=result.get('brands_mentioned', []),
            tools_mentioned=result.get('tools_mentioned', []),
            topics=result.get('topics', []),
            meta=result.get('meta', {}),
            
            # Processing metadata
            classified_at=datetime.now().isoformat(),
            classification_confidence=result.get('confidence', 0.5)
        )
    
    async def classify_content_item_with_retry(self, item: ContentItem) -> Optional[ClassifiedContent]:
        """Classify a content item with exponential backoff retry logic."""
        for attempt in range(self.max_retries):
            result = await self.classify_content_item(item)
            if result is not None:
                return result
            
            # Exponential backoff for retries
            if attempt < self.max_retries - 1:
                backoff_delay = self.rate_limit_delay * (self.backoff_factor ** attempt)
                self.logger.warning(f"Retrying item {item.id[:8]} (attempt {attempt + 1}/{self.max_retries}), waiting {backoff_delay:.1f}s")
                await asyncio.sleep(backoff_delay)
        
        self.logger.error(f"Failed to classify item {item.id[:8]} after {self.max_retries} attempts")
        return None

    async def classify_content_batch(self, items: List[ContentItem], 
                                   batch_size: int = 10) -> List[ClassifiedContent]:
        """Classify multiple content items sequentially with rate limiting."""
        classified_items = []
        
        self.logger.info(f"Classifying {len(items)} content items with batch size {batch_size}")
        
        for i, item in enumerate(items):
            self.logger.info(f"Processing item {i + 1}/{len(items)}: {item.id[:8] if item.id else 'no-id'}")
            
            result = await self.classify_content_item_with_retry(item)
            if result:
                classified_items.append(result)
            
            # Progress update every 10 items
            if (i + 1) % 10 == 0:
                self.logger.info(f"Progress: {i + 1}/{len(items)} items processed, "
                               f"{len(classified_items)} successfully classified")
        
        self.logger.info(f"Classification complete: {len(classified_items)}/{len(items)} items classified")
        return classified_items
    
    def save_classified_content(self, classified_items: List[ClassifiedContent], 
                              output_path: Path) -> None:
        """Save classified content to JSON file."""
        try:
            # Convert to serializable format
            data = {
                "metadata": {
                    "total_items": len(classified_items),
                    "generated_at": datetime.now().isoformat(),
                    "classification_model": "claude-3-haiku-20240307"
                },
                "classified_content": [asdict(item) for item in classified_items]
            }
            
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            
            self.logger.info(f"Saved {len(classified_items)} classified items to {output_path}")
            
        except Exception as e:
            self.logger.error(f"Error saving classified content: {e}")


async def main():
    """Test the content classifier."""
    import re
    from .content_parser import ContentParser
    
    # Test with a small sample
    parser = ContentParser()
    classifier = ContentClassifier()
    
    # Parse some content
    data_dir = Path("data/markdown_current") 
    markdown_files = list(data_dir.glob("hkia_*.md"))
    
    if markdown_files:
        # Test with first file and limit items
        items = parser.parse_markdown_file(markdown_files[0])[:3]
        
        print(f"Testing classification with {len(items)} items...")
        
        classified = await classifier.classify_content_batch(items, batch_size=2)
        
        print(f"Classified {len(classified)} items:")
        for item in classified:
            print(f"\n--- {item.id} ---")
            print(f"Summary: {item.summary_1_3_sentences}")
            print(f"Type: {item.content_type}")
            print(f"Application: {item.application}")
            print(f"Key Learnings: {item.key_learnings[:2]}")
            print(f"Topics: {item.topics}")
            print(f"Brands: {item.brands_mentioned}")


if __name__ == "__main__":
    asyncio.run(main())