"""
Link Candidate Engine
Finds relevant content for internal linking
"""
import logging
from typing import List, Dict
from django.db import models
from igny8_core.business.content.models import Content

logger = logging.getLogger(__name__)


class CandidateEngine:
    """Finds link candidates for content"""
    
    def find_candidates(self, content: Content, max_candidates: int = 10) -> List[Dict]:
        """
        Find link candidates for a piece of content.
        
        Args:
            content: Content instance to find links for
            max_candidates: Maximum number of candidates to return
        
        Returns:
            List of candidate dicts with: {'content_id', 'title', 'url', 'relevance_score', 'anchor_text'}
        """
        if not content or not content.html_content:
            return []
        
        # Find relevant content from same account/site/sector
        relevant_content = self._find_relevant_content(content)
        
        # Score candidates based on relevance
        candidates = self._score_candidates(content, relevant_content)
        
        # Sort by score and return top candidates
        candidates.sort(key=lambda x: x.get('relevance_score', 0), reverse=True)
        
        return candidates[:max_candidates]
    
    def _find_relevant_content(self, content: Content) -> List[Content]:
        """Find relevant content from same account/site/sector"""
        # Stage 3: Use cluster mappings for better relevance
        from igny8_core.business.content.models import ContentClusterMap
        
        # Get content from same account, site, and sector
        queryset = Content.objects.filter(
            account=content.account,
            site=content.site,
            sector=content.sector,
            status__in=['draft', 'review', 'publish']
        ).exclude(id=content.id)
        
        # Stage 3: Prioritize content from same cluster
        content_clusters = ContentClusterMap.objects.filter(
            content=content
        ).values_list('cluster_id', flat=True)
        
        if content_clusters:
            # Find content mapped to same clusters
            cluster_content_ids = ContentClusterMap.objects.filter(
                cluster_id__in=content_clusters
            ).exclude(content=content).values_list('content_id', flat=True).distinct()
            
            # Prioritize cluster-matched content
            cluster_matched = queryset.filter(id__in=cluster_content_ids)
            other_content = queryset.exclude(id__in=cluster_content_ids)
            
            # Combine: cluster-matched first, then others
            return list(cluster_matched[:30]) + list(other_content[:20])
        
        # Fallback to keyword-based filtering
        if content.primary_keyword:
            queryset = queryset.filter(
                models.Q(primary_keyword__icontains=content.primary_keyword) |
                models.Q(secondary_keywords__icontains=content.primary_keyword)
            )
        
        return list(queryset[:50])  # Limit initial query
    
    def _score_candidates(self, content: Content, candidates: List[Content]) -> List[Dict]:
        """Score candidates based on relevance"""
        from igny8_core.business.content.models import ContentClusterMap
        
        # Stage 3: Get cluster mappings for content
        content_clusters = set(
            ContentClusterMap.objects.filter(content=content)
            .values_list('cluster_id', flat=True)
        )
        # Taxonomy matching using Content.taxonomy_terms M2M relationship
        content_taxonomies = set(
            content.taxonomy_terms.values_list('id', flat=True)
        )
        
        scored = []
        
        for candidate in candidates:
            score = 0
            
            # Stage 3: Cluster matching (highest priority)
            candidate_clusters = set(
                ContentClusterMap.objects.filter(content=candidate)
                .values_list('cluster_id', flat=True)
            )
            cluster_overlap = content_clusters & candidate_clusters
            if cluster_overlap:
                score += 50 * len(cluster_overlap)  # High weight for cluster matches
            
            # Stage 3: Taxonomy matching using M2M relationship
            candidate_taxonomies = set(
                candidate.taxonomy_terms.values_list('id', flat=True)
            )
            taxonomy_overlap = content_taxonomies & candidate_taxonomies
            if taxonomy_overlap:
                score += 20 * len(taxonomy_overlap)
            
            # Stage 3: Entity type matching
            if content.entity_type == candidate.entity_type:
                score += 15
            
            # Keyword overlap (medium weight)
            if content.primary_keyword and candidate.primary_keyword:
                if content.primary_keyword.lower() in candidate.primary_keyword.lower():
                    score += 20
                if candidate.primary_keyword.lower() in content.primary_keyword.lower():
                    score += 20
            
            # Secondary keywords overlap
            if content.secondary_keywords and candidate.secondary_keywords:
                overlap = set(content.secondary_keywords) & set(candidate.secondary_keywords)
                score += len(overlap) * 5
            
            # Category overlap
            if content.categories and candidate.categories:
                overlap = set(content.categories) & set(candidate.categories)
                score += len(overlap) * 3
            
            # Tag overlap
            if content.tags and candidate.tags:
                overlap = set(content.tags) & set(candidate.tags)
                score += len(overlap) * 2
            
            # Recency bonus (newer content gets slight boost)
            if candidate.generated_at:
                days_old = (content.generated_at - candidate.generated_at).days
                if days_old < 30:
                    score += 3
            
            if score > 0:
                scored.append({
                    'content_id': candidate.id,
                    'title': candidate.title or candidate.task.title if candidate.task else 'Untitled',
                    'url': f"/content/{candidate.id}/",  # Placeholder - actual URL depends on routing
                    'relevance_score': score,
                    'cluster_match': len(cluster_overlap) > 0,  # Stage 3: Flag cluster matches
                    'taxonomy_match': len(taxonomy_overlap) > 0,  # Stage 3: Flag taxonomy matches
                    'anchor_text': self._generate_anchor_text(candidate, content)
                })
        
        return scored
    
    def _generate_anchor_text(self, candidate: Content, source_content: Content) -> str:
        """Generate anchor text for link"""
        # Use primary keyword if available, otherwise use title
        if candidate.primary_keyword:
            return candidate.primary_keyword
        elif candidate.title:
            return candidate.title
        elif candidate.task and candidate.task.title:
            return candidate.task.title
        else:
            return "Learn more"

