"""
Natural Language Intent Detection for El Gringo CLI
=====================================================

Lightweight regex-based intent router. Detects what the user wants
to do from free-form text and extracts the target (file, URL, directory, text).

No ML dependencies — fast, predictable, zero overhead.
"""

import os
import re
from dataclasses import dataclass
from typing import Optional


@dataclass
class Intent:
    action: str        # "review", "browse", "verify", "explain", "debug", etc.
    target: str        # file path, URL, directory, or remaining text
    target_type: str   # "file", "directory", "url", "text"
    confidence: float  # 0.0-1.0
    raw_input: str     # original user input
    extra: str = ""    # additional instruction beyond the action+target


# ── URL detection ─────────────────────────────────────────────────────

_URL_PATTERN = re.compile(
    r'(https?://[^\s]+|localhost[:\d]*[^\s]*|127\.0\.0\.1[:\d]*[^\s]*|\w+://[^\s]+|[\w.-]+:\d{2,5}[/\w]*)',
    re.IGNORECASE,
)


def _extract_url(text: str) -> Optional[str]:
    """Find a URL in text, including localhost:PORT patterns."""
    m = _URL_PATTERN.search(text)
    if m:
        url = m.group(1).rstrip(".,;:!?)")
        # Normalize bare host:port
        if not url.startswith(("http://", "https://")) and re.match(r'[\w.-]+:\d+', url):
            url = "http://" + url
        return url
    return None


# ── File/directory detection ──────────────────────────────────────────

_PATH_PATTERN = re.compile(
    r'(?:^|\s)((?:\.{0,2}/)?[\w./-]+\.(?:py|js|ts|tsx|jsx|go|rs|java|rb|vue|css|html|json|yaml|yml|toml|sh|md|txt|cfg|ini|sql|c|cpp|h))\b'
)

_DIR_PATTERN = re.compile(
    r'(?:^|\s)((?:\.{0,2}/)?[\w./-]+/)\s'
)


def _extract_path(text: str) -> Optional[str]:
    """Find a file or directory path in text."""
    # Check for explicit file extensions first
    m = _PATH_PATTERN.search(text)
    if m:
        path = m.group(1)
        if os.path.exists(path):
            return path
        # File with extension mentioned but not in cwd — search recursively
        filename = os.path.basename(path)
        found = _find_file_recursive(filename)
        if found:
            return found

    # Check for directory-like patterns (ending with /)
    m = _DIR_PATTERN.search(text + " ")  # pad for trailing space match
    if m:
        path = m.group(1)
        if os.path.isdir(path):
            return path

    # Try to find bare words that match files/dirs in cwd
    # Skip common English words that happen to be directory names
    _stop_words = {'the', 'a', 'an', 'is', 'for', 'to', 'in', 'on', 'at', 'of', 'my',
                   'it', 'if', 'and', 'or', 'not', 'with', 'from', 'tests', 'test',
                   'code', 'file', 'check', 'add', 'write', 'run', 'make', 'build'}
    words = text.split()
    for word in reversed(words):  # check from end — target is usually last
        word = word.strip("\"'`,;:!?()")
        if word.lower() in _stop_words:
            continue
        if os.path.exists(word):
            return word

    return None


def _find_file_recursive(filename: str, max_depth: int = 4) -> Optional[str]:
    """Search for a file by name in the project tree. Returns closest match."""
    cwd = os.getcwd()
    best_match = None
    best_depth = 999

    for root, dirs, files in os.walk(cwd):
        dirs[:] = [d for d in dirs if not d.startswith('.') and d not in
                   {'node_modules', '__pycache__', 'venv', '.venv', 'dist', 'build', 'egg-info'}]
        depth = root.replace(cwd, '').count(os.sep)
        if depth > max_depth:
            continue
        if filename in files and depth < best_depth:
            best_match = os.path.relpath(os.path.join(root, filename), cwd)
            best_depth = depth

    return best_match


def _resolve_module_name(text: str) -> Optional[str]:
    """Fuzzy-match module/folder names like 'auth module' to actual paths."""
    # Extract likely module name keywords
    keywords = []
    for pattern in [
        r'(?:the\s+)?(\w+)\s+(?:module|folder|directory|dir|package|component|service)',
        r'(?:in|from|at)\s+(?:the\s+)?(\w+)',
        r'(?:module|folder|directory|dir|package)\s+(\w+)',
    ]:
        m = re.search(pattern, text, re.IGNORECASE)
        if m:
            keywords.append(m.group(1).lower())

    if not keywords:
        return None

    # Search cwd for matching files/directories
    cwd = os.getcwd()
    best_match = None
    best_score = -1  # higher is better

    for root, dirs, files in os.walk(cwd):
        # Skip hidden/build dirs
        dirs[:] = [d for d in dirs if not d.startswith('.') and d not in
                   {'node_modules', '__pycache__', 'venv', '.venv', 'dist', 'build', 'egg-info'}]

        depth = root.replace(cwd, '').count(os.sep)
        if depth > 3:
            continue

        rel_root = os.path.relpath(root, cwd)

        for keyword in keywords:
            # Check directory name — prefer exact match, directories score higher
            dirname = os.path.basename(root).lower()
            if keyword == dirname:
                score = 100 - depth  # exact dir match, best possible
                if score > best_score:
                    best_match = rel_root
                    best_score = score
            elif keyword in dirname:
                score = 50 - depth  # substring dir match
                if score > best_score:
                    best_match = rel_root
                    best_score = score

            # Check file names — exact stem match scores high
            for f in files:
                fname = f.lower()
                stem = os.path.splitext(fname)[0]
                if keyword == stem:
                    score = 90 - depth  # exact file stem match
                    if score > best_score:
                        best_match = os.path.join(rel_root, f)
                        best_score = score
                elif keyword in fname:
                    score = 40 - depth  # substring file match
                    if score > best_score:
                        best_match = os.path.join(rel_root, f)
                        best_score = score

    return best_match


# ── Intent patterns ───────────────────────────────────────────────────
# Each entry: (compiled_regex, action, confidence_boost)
# Patterns are checked in order; first match wins.

_INTENT_PATTERNS = [
    # Browse / web
    (re.compile(r'\b(?:browse|open|visit|go\s+to|navigate|fetch|load)\b.*(?:http|localhost|:\d{2,5}|\.com|\.io|\.dev|\.app)', re.I),
     "browse", 0.9),
    (re.compile(r'\b(?:browse|open|visit|go\s+to|navigate)\b', re.I),
     "browse", 0.7),

    # Verify / health check
    (re.compile(r'\b(?:is\s+(?:my\s+)?(?:server|app|api|site)\s+(?:running|up|working|alive))', re.I),
     "verify", 0.85),
    (re.compile(r'\b(?:start\s+.*?\s+and\s+(?:check|test|verify))', re.I),
     "verify", 0.85),
    (re.compile(r'\b(?:verify|health\s*check|smoke\s*test|check\s+(?:if|whether))\b', re.I),
     "verify", 0.8),
    (re.compile(r'\b(?:test|check)\s+(?:my\s+)?(?:server|app|api|endpoint|site|service)\b', re.I),
     "verify", 0.75),

    # Review
    (re.compile(r'\b(?:review|audit|inspect|check|look\s+at|examine)\s+(?:the\s+)?(?:code|file|files|module|folder|directory|dir|src|lib|app)', re.I),
     "review", 0.85),
    (re.compile(r'\b(?:review|code\s+review)\b', re.I),
     "review", 0.75),

    # Security
    (re.compile(r'\b(?:secur(?:e|ity)|vulnerabilit|CVE|OWASP|pen\s*test|exploit)', re.I),
     "secure", 0.85),

    # Debug
    (re.compile(r'\b(?:debug|what\'?s\s+wrong|fix\s+(?:the\s+)?(?:error|bug|issue)|traceback|exception|TypeError|ValueError|KeyError|ImportError)', re.I),
     "debug", 0.8),

    # Explain
    (re.compile(r'\b(?:explain|what\s+does|how\s+does|walk\s+(?:me\s+)?through|describe|break\s*down)\b.*(?:code|file|function|class|module|method|do\b|work|\.py|\.js|\.ts|\.go|\.rs)', re.I),
     "explain", 0.8),
    (re.compile(r'\b(?:explain|describe)\b', re.I),
     "explain", 0.65),

    # Test — "write tests for X" (extract target after "for")
    (re.compile(r'\b(?:write\s+tests?\s+for|add\s+tests?\s+for|generate\s+tests?\s+for)\b', re.I),
     "test", 0.85),
    (re.compile(r'\b(?:write\s+tests?|add\s+tests?|generate\s+tests?|test\s+(?:the\s+)?(?:code|file|module|function))', re.I),
     "test", 0.8),

    # Optimize
    (re.compile(r'\b(?:optimiz|speed\s+up|performance|make\s+(?:it\s+)?faster|slow|profil)', re.I),
     "optimize", 0.8),

    # Refactor
    (re.compile(r'\b(?:refactor|clean\s+up|restructur|reorganiz|simplif)', re.I),
     "refactor", 0.8),
]


def detect_intent(user_input: str) -> Optional[Intent]:
    """Detect user intent from natural language input.

    Returns an Intent if a clear action is detected with confidence > 0.5,
    or None if the input should be handled as a general AI request.
    """
    text = user_input.strip()
    if not text:
        return None

    # Try to match an action pattern
    matched_action = None
    matched_confidence = 0.0

    for pattern, action, confidence in _INTENT_PATTERNS:
        if pattern.search(text):
            matched_action = action
            matched_confidence = confidence
            break

    if not matched_action or matched_confidence < 0.5:
        return None

    # Extract target based on action type
    target = ""
    target_type = "text"
    extra = ""

    if matched_action == "browse":
        url = _extract_url(text)
        if url:
            target = url
            target_type = "url"
            # Extract instruction after the URL
            url_end = text.find(url) + len(url) if url in text else len(text)
            remaining = text[url_end:].strip().strip('"\'')
            if remaining:
                extra = remaining
        else:
            # No URL found — lower confidence
            matched_confidence = 0.4

    elif matched_action == "verify":
        url = _extract_url(text)
        if url:
            target = url
            target_type = "url"
        else:
            # Try to find a directory
            path = _extract_path(text)
            if path and os.path.isdir(path):
                target = path
                target_type = "directory"
            else:
                # Check for port numbers
                port_m = re.search(r'\b(?:port\s+)?(\d{2,5})\b', text)
                if port_m:
                    target = f"http://localhost:{port_m.group(1)}"
                    target_type = "url"
                else:
                    target = "http://localhost:8000"
                    target_type = "url"
                    matched_confidence *= 0.7

    elif matched_action in ("review", "explain", "debug", "test", "optimize", "refactor", "secure"):
        # Try explicit path first
        path = _extract_path(text)
        if path:
            target = path
            target_type = "directory" if os.path.isdir(path) else "file"
        else:
            # Try fuzzy module name resolution
            resolved = _resolve_module_name(text)
            if resolved:
                target = resolved
                target_type = "directory" if os.path.isdir(resolved) else "file"
                matched_confidence *= 0.9  # slightly lower since it's fuzzy
            else:
                # No target found — pass the whole text, let it be a general prompt
                target = text
                target_type = "text"
                # Debug/explain can work without a file target (user describes the problem)
                if matched_action in ("debug", "explain"):
                    matched_confidence *= 0.8
                else:
                    matched_confidence *= 0.6

    if matched_confidence < 0.5:
        return None

    return Intent(
        action=matched_action,
        target=target,
        target_type=target_type,
        confidence=matched_confidence,
        raw_input=text,
        extra=extra,
    )
