"""Domain Understanding Engine for NIKAME.

Extracts entities, actions, and infrastructure signals from natural language descriptions.
"""

from __future__ import annotations

import re

from nikame.config.schema import (
    APIConfig,
    AuthConfig,
    CacheConfig,
    DatabasesConfig,
    DataModelConfig,
    FieldConfig,
    MessagingConfig,
    NikameConfig,
    StorageConfig,
)


class DomainUnderstandingEngine:
    """Analyze project descriptions to suggest a technical stack and data model."""

    # Keywords that signal specific infrastructure components
    INFRA_SIGNALS = {
        "database": ["postgres", "sql", "data", "store", "persist", "db", "database"],
        "cache": ["redis", "dragonfly", "performance", "speed", "fast", "session", "cache"],
        "messaging": ["kafka", "redpanda", "stream", "message", "async", "queue", "pubsub", "nats", "rabbitmq", "chat", "real-time", "realtime"],
        "storage": ["s3", "minio", "bucket", "upload", "file", "image", "blob", "storage", "media"],
        "auth": ["login", "user", "security", "auth", "identity", "jwt", "keycloak", "signup", "user", "profile"],
        "search": ["search", "find", "vector", "rag", "qdrant", "elasticsearch", "milvus", "embedding"],
        "ml": ["ai", "ml", "model", "inference", "llm", "gpt", "rag", "serving", "ollama"],
    }

    # Stop words to ignore during entity extraction
    STOP_WORDS = {
        "a", "an", "the", "for", "with", "and", "or", "of", "to", "in", "is", "at", 
        "by", "from", "up", "platform", "system", "app", "application", "service",
        "backend", "api", "tool", "framework", "software", "simple", "real", "time", 
        "real-time", "realtime", "using", "built", "this", "that"
    }

    def analyze(self, description: str) -> NikameConfig:
        """Analyze description and return a candidate NikameConfig."""
        desc_lower = description.lower()
        # Handle hyphenated words like real-time
        words = set(re.findall(r'[\w-]+', desc_lower))
        
        # 1. Initialize empty config
        config = NikameConfig(
            name="generated-project",
            description=description
        )

        # 2. Extract Infra Signals
        signals = self._get_signals(words)
        
        if "database" in signals or "postgres" in desc_lower:
            config.databases = DatabasesConfig(postgres={"version": "16"})
            
        if "cache" in signals or "redis" in desc_lower or "dragonfly" in desc_lower:
            config.cache = CacheConfig(provider="dragonfly")
            
        if "messaging" in signals:
            config.messaging = MessagingConfig(redpanda={"brokers": 1})
            
        if "storage" in signals:
            config.storage = StorageConfig(provider="minio")
            
        if "auth" in signals:
            config.auth = AuthConfig(provider="postgres")
            config.api = APIConfig(auth={"enabled": True})
        else:
            config.api = APIConfig()

        if "search" in signals or "vector" in desc_lower:
            if not config.databases:
                config.databases = DatabasesConfig()
            config.databases.qdrant = {"enabled": True}

        # 3. Extract Potential Entities
        entities = self._extract_entities(desc_lower)
        for entity in entities:
            sanitized_entity = self._sanitize_name(entity)
            config.models[sanitized_entity] = DataModelConfig(
                fields={
                    "id": FieldConfig(type="int", required=True, unique=True),
                    "name": FieldConfig(type="str", required=True),
                    "created_at": FieldConfig(type="datetime", default="now")
                }
            )

        return config

    def _sanitize_name(self, name: str) -> str:
        """Sanitize name to be a valid Python identifier."""
        # Replace hyphens and spaces with underscores
        s = re.sub(r'[-\s]+', '_', name)
        # Remove any other non-alphanumeric characters except underscores
        s = re.sub(r'[^\w]', '', s)
        # Ensure it doesn't start with a number
        if s and s[0].isdigit():
            s = "_" + s
        return s

    def _get_signals(self, words: set[str]) -> set[str]:
        """Map words to infrastructure types."""
        detected = set()
        for infra_type, keywords in self.INFRA_SIGNALS.items():
            if any(kw in words for kw in keywords):
                detected.add(infra_type)
        return detected

    def _extract_entities(self, text: str) -> set[str]:
        """Simple noun-like entity extraction."""
        # Find all words, keeping hyphenated ones
        words = re.findall(r'[\w-]+', text)
        potential = [w for w in words if w not in self.STOP_WORDS and len(w) > 3]
        
        # Rank by frequency or just position (simplified)
        seen = set()
        entities = []
        for word in potential:
            if word not in seen:
                entities.append(word)
                seen.add(word)
            if len(entities) >= 5:
                break
        return set(entities)
