"""
MTG Card Interaction Recommender

Generates card recommendations based on interaction data.
Provides synergy suggestions, archetype cards, and card-like-this recommendations.
"""
from typing import List, Dict, Optional
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker

from card_profile_extractor import CardProfileExtractor
from interaction_determinator import InteractionDeterminator


class InteractionRecommender:
    """
    Generates card recommendations based on interaction data.
    
    Provides:
    - Synergy recommendations for a specific card
    - Archetype cards for a given archetype
    - Similar cards based on profiles
    - Deck building suggestions
    """
    
    def __init__(self, db_url: str):
        """
        Initialize the recommender.
        
        Args:
            db_url: PostgreSQL database URL
        """
        self.db_url = db_url
        self.engine = create_engine(db_url)
        self.SessionLocal = sessionmaker(bind=self.engine)
        self.profile_extractor = CardProfileExtractor()
        self.determinator = InteractionDeterminator()
    
    def get_card_profile(self, card_id: int) -> Optional[CardProfile]:
        """
        Get a card profile from the database.
        
        Args:
            card_id: Card ID
            
        Returns:
            CardProfile or None if not found
        """
        db = self.SessionLocal()
        try:
            query = text("""
                SELECT c.*, s.code as set_code
                FROM mtg_cards c
                JOIN mtg_sets s ON c.set_id = s.id
                WHERE c.id = :card_id
            """)
            
            result = db.execute(query, {"card_id": card_id}).fetchone()
            
            if result:
                card_data = dict(result._mapping)
                # Extract profile from raw data
                profile = self.profile_extractor.extract_profile(card_data)
                return profile
            return None
        finally:
            db.close()
    
    def get_card_by_id(self, card_id: int) -> Optional[Dict]:
        """
        Get raw card data from the database.
        
        Args:
            card_id: Card ID
            
        Returns:
            Card dictionary or None if not found
        """
        db = self.SessionLocal()
        try:
            query = text("""
                SELECT c.*, s.code as set_code, s.name as set_name
                FROM mtg_cards c
                JOIN mtg_sets s ON c.set_id = s.id
                WHERE c.id = :card_id
            """)
            
            result = db.execute(query, {"card_id": card_id}).fetchone()
            
            if result:
                return dict(result._mapping)
            return None
        finally:
            db.close()
    
    def get_synergies_for_card(self, card_id: int) -> List[Dict]:
        """
        Get synergy data for a card from the database.
        
        Args:
            card_id: Card ID
            
        Returns:
            List of synergy dictionaries
        """
        db = self.SessionLocal()
        try:
            query = text("""
                SELECT cs.*, 
                       ca.name as card_a_name, ca.type_line as card_a_type_line,
                       cb.name as card_b_name, cb.type_line as card_b_type_line
                FROM mtg_card_synergies cs
                JOIN mtg_cards ca ON cs.card_a_id = ca.id
                JOIN mtg_cards cb ON cs.card_b_id = cb.id
                WHERE cs.card_a_id = :card_id OR cs.card_b_id = :card_id
                ORDER BY cs.strength DESC, cs.confidence DESC
                LIMIT :limit
            """)
            
            results = db.execute(query, {
                "card_id": card_id,
                "limit": 100
            }).fetchall()
            
            return [dict(row._mapping) for row in results]
        finally:
            db.close()
    
    def get_counters_for_card(self, card_id: int) -> List[Dict]:
        """
        Get counter data for a card from the database.
        
        Args:
            card_id: Card ID
            
        Returns:
            List of counter dictionaries
        """
        db = self.SessionLocal()
        try:
            query = text("""
                SELECT cc.*, 
                       ca.name as card_a_name, ca.type_line as card_a_type_line,
                       cb.name as card_b_name, cb.type_line as card_b_type_line
                FROM mtg_card_counters cc
                JOIN mtg_cards ca ON cc.card_a_id = ca.id
                JOIN mtg_cards cb ON cc.card_b_id = cb.id
                WHERE cc.card_a_id = :card_id OR cc.card_b_id = :card_id
                ORDER BY cc.strength DESC, cc.confidence DESC
                LIMIT :limit
            """)
            
            results = db.execute(query, {
                "card_id": card_id,
                "limit": 100
            }).fetchall()
            
            return [dict(row._mapping) for row in results]
        finally:
            db.close()
    
    def recommend_synergies(self, card_id: int, max_results: int = 20) -> List[Dict]:
        """
        Recommend cards that synergize with a given card.
        
        Args:
            card_id: Card ID to find synergies for
            max_results: Maximum number of recommendations
            
        Returns:
            List of recommendation dictionaries
        """
        synergies = self.get_synergies_for_card(card_id)
        
        recommendations = []
        seen_cards = set()
        
        for synergy in synergies:
            # Determine which card is the "other" card
            if synergy['card_a_id'] == card_id:
                other_card_id = synergy['card_b_id']
                other_card_name = synergy['card_b_name']
                other_card_type = synergy['card_b_type_line']
            else:
                other_card_id = synergy['card_a_id']
                other_card_name = synergy['card_a_name']
                other_card_type = synergy['card_a_type_line']
            
            # Skip if already seen
            if other_card_id in seen_cards:
                continue
            seen_cards.add(other_card_id)
            
            recommendations.append({
                'card_id': other_card_id,
                'card_name': other_card_name,
                'card_type_line': other_card_type,
                'synergy_type': synergy['synergy_type'],
                'strength': synergy['strength'],
                'confidence': synergy['confidence'],
                'notes': synergy['notes'],
                'recommendation_type': 'synergy',
            })
        
        # Sort by strength and confidence
        recommendations.sort(key=lambda r: (r['strength'], r['confidence']), reverse=True)
        
        return recommendations[:max_results]
    
    def recommend_archetype_cards(self, archetype: str, max_results: int = 20) -> List[Dict]:
        """
        Recommend cards that fit a specific archetype.
        
        Args:
            archetype: Archetype name (e.g., 'goblin', 'elf')
            max_results: Maximum number of recommendations
            
        Returns:
            List of recommendation dictionaries
        """
        db = self.SessionLocal()
        try:
            query = text("""
                SELECT c.*, s.code as set_code, s.name as set_name
                FROM mtg_cards c
                JOIN mtg_sets s ON c.set_id = s.id
                WHERE c.subtypes LIKE :archetype
                ORDER BY c.id
                LIMIT :limit
            """)
            
            results = db.execute(query, {
                "archetype": f"%{archetype}%",
                "limit": max_results
            }).fetchall()
            
            recommendations = []
            for result in results:
                card_data = dict(result._mapping)
                recommendations.append({
                    'card_id': card_data['id'],
                    'card_name': card_data['name'],
                    'card_type_line': card_data['type_line'],
                    'set_code': card_data['set_code'],
                    'set_name': card_data['set_name'],
                    'recommendation_type': 'archetype',
                    'archetype': archetype,
                    'confidence': 0.8,
                    'notes': f"Matches {archetype} archetype",
                })
            
            return recommendations
        finally:
            db.close()
    
    def recommend_similar_cards(self, card_id: int, max_results: int = 20) -> List[Dict]:
        """
        Recommend cards similar to a given card.
        
        Args:
            card_id: Card ID to find similar cards for
            max_results: Maximum number of recommendations
            
        Returns:
            List of recommendation dictionaries
        """
        card_data = self.get_card_by_id(card_id)
        
        if not card_data:
            return []
        
        profile = self.profile_extractor.extract_profile(card_data)
        
        # Get similar cards based on archetype and mechanics
        db = self.SessionLocal()
        try:
            recommendations = []
            seen_cards = set()
            
            # Get cards with matching archetypes
            if profile.archetypes:
                for archetype in profile.archetypes:
                    query = text("""
                        SELECT c.*, s.code as set_code
                        FROM mtg_cards c
                        JOIN mtg_sets s ON c.set_id = s.id
                        WHERE c.subtypes LIKE :archetype
                        AND c.id != :card_id
                        LIMIT :limit
                    """)
                    
                    results = db.execute(query, {
                        "archetype": f"%{archetype}%",
                        "card_id": card_id,
                        "limit": max_results * 2
                    }).fetchall()
                    
                    for result in results:
                        card = dict(result._mapping)
                        if card['id'] not in seen_cards:
                            seen_cards.add(card['id'])
                            recommendations.append({
                                'card_id': card['id'],
                                'card_name': card['name'],
                                'card_type_line': card['type_line'],
                                'set_code': card['set_code'],
                                'recommendation_type': 'similar',
                                'reason': f"Same archetype: {archetype}",
                                'confidence': 0.7,
                            })
            
            # Get cards with matching mechanics
            if profile.mechanics:
                for mechanic in profile.mechanics[:3]:  # Limit to top 3 mechanics
                    query = text("""
                        SELECT c.*, s.code as set_code
                        FROM mtg_cards c
                        JOIN mtg_sets s ON c.set_id = s.id
                        WHERE c.oracle_text LIKE :mechanic
                        AND c.id != :card_id
                        LIMIT :limit
                    """)
                    
                    results = db.execute(query, {
                        "mechanic": f"%{mechanic}%",
                        "card_id": card_id,
                        "limit": max_results
                    }).fetchall()
                    
                    for result in results:
                        card = dict(result._mapping)
                        if card['id'] not in seen_cards:
                            seen_cards.add(card['id'])
                            recommendations.append({
                                'card_id': card['id'],
                                'card_name': card['name'],
                                'card_type_line': card['type_line'],
                                'set_code': card['set_code'],
                                'recommendation_type': 'similar',
                                'reason': f"Has mechanic: {mechanic}",
                                'confidence': 0.6,
                            })
            
            # Sort by confidence
            recommendations.sort(key=lambda r: r['confidence'], reverse=True)
            
            return recommendations[:max_results]
        finally:
            db.close()
    
    def get_deck_recommendations(self, card_id: int, max_results: int = 10) -> Dict:
        """
        Get deck building recommendations for a card.
        
        Args:
            card_id: Card ID
            max_results: Maximum number of recommendations
            
        Returns:
            Dictionary with synergy cards, archetype cards, and similar cards
        """
        # Get synergy cards
        synergy_cards = self.recommend_synergies(card_id, max_results)
        
        # Get archetype cards
        card_data = self.get_card_by_id(card_id)
        archetype_cards = []
        
        if card_data and card_data.get('subtypes'):
            # Extract first archetype
            archetypes = [a.strip() for a in card_data['subtypes'].split(',')]
            if archetypes:
                archetype_cards = self.recommend_archetype_cards(archetypes[0], max_results)
        
        # Get similar cards
        similar_cards = self.recommend_similar_cards(card_id, max_results)
        
        return {
            'synergy_cards': synergy_cards,
            'archetype_cards': archetype_cards,
            'similar_cards': similar_cards,
            'total_recommendations': len(synergy_cards) + len(archetype_cards) + len(similar_cards),
        }
    
    def close(self):
        """Close database connection."""
        self.engine.dispose()
