"""
Agent Template Registry

Central registry for all agent templates with lookup and selection functionality.
"""

from typing import List, Dict, Optional, Set
from dataclasses import dataclass

from .base import AgentTemplate, AgentTeamComposition, AgentRole
from .creative import CREATIVE_TEMPLATES
from .technical import TECHNICAL_TEMPLATES
from .research import RESEARCH_TEMPLATES
from ...intelligence.domain_analyzer import TaskDomain, DomainAnalysis


@dataclass
class TemplateMatch:
    """A template matched against requirements."""
    template: AgentTemplate
    relevance_score: float  # 0.0 to 1.0
    matched_domains: List[TaskDomain]


class TemplateRegistry:
    """
    Registry for agent templates with intelligent selection.

    Provides lookup by ID, domain, and capability matching.
    """

    def __init__(self):
        self._templates: Dict[str, AgentTemplate] = {}
        self._by_domain: Dict[TaskDomain, List[AgentTemplate]] = {}
        self._register_defaults()

    def _register_defaults(self):
        """Register all default templates."""
        all_templates = CREATIVE_TEMPLATES + TECHNICAL_TEMPLATES + RESEARCH_TEMPLATES

        for template in all_templates:
            self.register(template)

    def register(self, template: AgentTemplate):
        """Register a new template."""
        self._templates[template.id] = template

        # Index by primary domain
        if template.domain not in self._by_domain:
            self._by_domain[template.domain] = []
        self._by_domain[template.domain].append(template)

        # Index by secondary domains
        for domain in template.secondary_domains:
            if domain not in self._by_domain:
                self._by_domain[domain] = []
            if template not in self._by_domain[domain]:
                self._by_domain[domain].append(template)

    def get(self, template_id: str) -> Optional[AgentTemplate]:
        """Get a template by ID."""
        return self._templates.get(template_id)

    def list_all(self) -> List[AgentTemplate]:
        """Get all registered templates."""
        return list(self._templates.values())

    def list_by_domain(self, domain: TaskDomain) -> List[AgentTemplate]:
        """Get all templates for a specific domain."""
        return self._by_domain.get(domain, [])

    def find_by_capability(self, capability: str) -> List[AgentTemplate]:
        """Find templates that have a specific capability."""
        results = []
        capability_lower = capability.lower()

        for template in self._templates.values():
            for cap in template.capabilities:
                if capability_lower in cap.name.lower():
                    results.append(template)
                    break

        return results

    def search(self, query: str) -> List[AgentTemplate]:
        """Search templates by name, description, or tags."""
        query_lower = query.lower()
        results = []

        for template in self._templates.values():
            # Check name
            if query_lower in template.name.lower():
                results.append(template)
                continue

            # Check description
            if query_lower in template.description.lower():
                results.append(template)
                continue

            # Check tags
            if any(query_lower in tag.lower() for tag in template.tags):
                results.append(template)
                continue

        return results

    def select_for_analysis(
        self,
        analysis: DomainAnalysis,
        max_agents: int = 5
    ) -> List[TemplateMatch]:
        """
        Select best templates based on domain analysis.

        Args:
            analysis: The domain analysis result
            max_agents: Maximum number of agents to suggest

        Returns:
            List of template matches sorted by relevance
        """
        matches: List[TemplateMatch] = []
        seen_ids: Set[str] = set()
        task_text = analysis.task_text.lower()

        # Get templates for each detected domain
        for domain_match in analysis.all_domains:
            if domain_match.confidence < 0.3:
                continue

            templates = self.list_by_domain(domain_match.domain)

            for template in templates:
                if template.id in seen_ids:
                    continue
                seen_ids.add(template.id)

                # Calculate relevance score
                relevance = self._calculate_relevance(
                    template,
                    domain_match.domain,
                    domain_match.confidence
                )

                # Apply task-specific adjustments for technical coding
                if domain_match.domain == TaskDomain.TECHNICAL_CODING:
                    relevance = self._adjust_for_task_content(
                        template, task_text, relevance
                    )

                matches.append(TemplateMatch(
                    template=template,
                    relevance_score=relevance,
                    matched_domains=[domain_match.domain]
                ))

        # Sort by relevance
        matches.sort(key=lambda m: m.relevance_score, reverse=True)

        return matches[:max_agents]

    def _adjust_for_task_content(
        self,
        template: AgentTemplate,
        task_text: str,
        base_relevance: float
    ) -> float:
        """
        Adjust relevance based on specific task content for technical coding.

        Args:
            template: The template to adjust
            task_text: Lowercase task description
            base_relevance: Starting relevance score

        Returns:
            Adjusted relevance score
        """
        import re

        # Backend indicators
        backend_patterns = [
            r'\bapi\b', r'\brest\b', r'\bendpoint\b', r'\bserver\b',
            r'\bbackend\b', r'\bdatabase\b', r'\bauthentication\b',
            r'\bfastapi\b', r'\bdjango\b', r'\bflask\b', r'\bexpress\b'
        ]

        # Frontend indicators
        frontend_patterns = [
            r'\breact\b', r'\bvue\b', r'\bangular\b', r'\bui\b',
            r'\bfrontend\b', r'\bcss\b', r'\bcomponent\b', r'\bstyle\b',
            r'\btailwind\b', r'\blayout\b'
        ]

        has_backend_keywords = any(
            re.search(p, task_text) for p in backend_patterns
        )
        has_frontend_keywords = any(
            re.search(p, task_text) for p in frontend_patterns
        )

        # Adjust based on template type and task content
        if template.id == "backend-specialist":
            if has_backend_keywords and not has_frontend_keywords:
                base_relevance *= 1.5  # Strong boost for backend-specific tasks
            elif has_frontend_keywords and not has_backend_keywords:
                base_relevance *= 0.5  # Penalty for frontend-specific tasks

        elif template.id == "frontend-specialist":
            if has_frontend_keywords and not has_backend_keywords:
                base_relevance *= 1.5  # Strong boost for frontend-specific tasks
            elif has_backend_keywords and not has_frontend_keywords:
                base_relevance *= 0.5  # Penalty for backend-specific tasks

        return min(1.0, base_relevance)

    def compose_team(
        self,
        analysis: DomainAnalysis
    ) -> AgentTeamComposition:
        """
        Compose an optimal team for the given task analysis.

        Args:
            analysis: The domain analysis result

        Returns:
            Recommended team composition
        """
        matches = self.select_for_analysis(analysis, max_agents=5)

        if not matches:
            # Fallback to a general-purpose agent
            fallback = self.get("fullstack-developer") or self.list_all()[0]
            return AgentTeamComposition(lead=fallback)

        # First match is the lead
        lead = matches[0].template
        specialists = []
        reviewers = []

        # Assign remaining agents based on their default roles
        for match in matches[1:]:
            template = match.template
            if template.default_role == AgentRole.REVIEWER:
                reviewers.append(template)
            else:
                specialists.append(template)

        # Limit team size based on complexity
        max_specialists = min(2, analysis.suggested_team_size - 1)
        max_reviewers = 1 if analysis.complexity != "simple" else 0

        return AgentTeamComposition(
            lead=lead,
            specialists=specialists[:max_specialists],
            reviewers=reviewers[:max_reviewers],
        )

    def _calculate_relevance(
        self,
        template: AgentTemplate,
        domain: TaskDomain,
        domain_confidence: float
    ) -> float:
        """Calculate relevance score for a template."""
        base_score = domain_confidence

        # Boost if this is the template's primary domain
        if template.domain == domain:
            base_score *= 1.3

        # Penalize generalist agents (fullstack, researcher) in favor of specialists
        generalist_ids = ["fullstack-developer"]
        if template.id in generalist_ids:
            base_score *= 0.7  # Reduce score to prefer specialists

        # Slight boost for templates designed to be specialists in their domain
        if template.default_role == AgentRole.SPECIALIST:
            base_score *= 1.05

        return min(1.0, base_score)


# Global registry instance
_registry: Optional[TemplateRegistry] = None


def get_registry() -> TemplateRegistry:
    """Get or create the global template registry."""
    global _registry
    if _registry is None:
        _registry = TemplateRegistry()
    return _registry


def get_template(template_id: str) -> Optional[AgentTemplate]:
    """Convenience function to get a template by ID."""
    return get_registry().get(template_id)


def list_templates() -> List[AgentTemplate]:
    """Convenience function to list all templates."""
    return get_registry().list_all()


def select_agents_for_task(analysis: DomainAnalysis) -> List[TemplateMatch]:
    """Convenience function to select agents for a task."""
    return get_registry().select_for_analysis(analysis)


def compose_team_for_task(analysis: DomainAnalysis) -> AgentTeamComposition:
    """Convenience function to compose a team for a task."""
    return get_registry().compose_team(analysis)
