"""
Autocomplete System - @ trigger with frecency scoring

Provides intelligent autocomplete for:
- @filename - File fuzzy search with frecency
- @agent - Agent type suggestions
- @mcp:resource - MCP resource references
- Line range syntax: @file.py#10-20

Examples:
    # Create autocomplete provider
    provider = AutocompleteProvider()

    # Detect @ trigger
    if provider.should_trigger("@main"):
        suggestions = provider.get_suggestions("@main")
        # Returns: [@main.py, @main.ts, @maintain.py, ...]

    # With frecency scoring
    provider.record_access("src/main.py")
    suggestions = provider.get_suggestions("@main")  # main.py ranks higher

    # Line range
    suggestions = provider.get_suggestions("@main.py#")
    # Suggests: @main.py#1-10, @main.py#10-20, etc.
"""

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Optional
import json
import time

from groknroll.tui.fuzzy_search import FuzzyFileSearch, FuzzyMatch


class AutocompleteType(Enum):
    """Type of autocomplete suggestion"""
    FILE = "file"
    AGENT = "agent"
    MCP_RESOURCE = "mcp_resource"
    LINE_RANGE = "line_range"


@dataclass
class AutocompleteSuggestion:
    """An autocomplete suggestion"""
    text: str  # Full text to insert (e.g., "@main.py")
    display: str  # Display text (e.g., "main.py")
    type: AutocompleteType
    score: float  # Frecency score (0-100)
    description: str = ""
    metadata: dict = field(default_factory=dict)

    def __lt__(self, other: "AutocompleteSuggestion") -> bool:
        """Sort by score (descending)"""
        return self.score > other.score


@dataclass
class FrecencyRecord:
    """Frecency tracking record for a file"""
    path: str
    access_count: int = 0
    last_accessed: float = 0.0  # Unix timestamp
    total_score: float = 0.0

    def record_access(self):
        """Record a file access"""
        self.access_count += 1
        self.last_accessed = time.time()
        self.update_score()

    def update_score(self):
        """Update frecency score: frequency × recency_weight"""
        if self.last_accessed == 0:
            self.total_score = 0.0
            return

        # Calculate time since last access (in days)
        days_ago = (time.time() - self.last_accessed) / 86400

        # Recency weight (decays over time)
        # 1.0 if accessed today, ~0.5 after 7 days, ~0.1 after 30 days
        recency_weight = 1.0 / (1.0 + days_ago)

        # Frecency = frequency × recency
        self.total_score = self.access_count * recency_weight * 10.0  # Scale to 0-100


class AutocompleteProvider:
    """
    Provide @ autocomplete with frecency scoring

    Supports multiple @ trigger types:
    - @filename - File fuzzy search
    - @agent - Agent suggestions
    - @mcp:resource - MCP resource references
    - @file#L - Line range syntax
    """

    AGENT_TYPES = [
        ("build", "Full coding access with all tools"),
        ("plan", "Read-only analysis and planning"),
        ("oracle", "Codebase knowledge and Q&A"),
    ]

    def __init__(
        self,
        workspace_root: Optional[Path] = None,
        frecency_file: Optional[Path] = None,
    ):
        """
        Initialize autocomplete provider

        Args:
            workspace_root: Root directory for file search
            frecency_file: Path to frecency tracking file
        """
        self.workspace_root = workspace_root or Path.cwd()
        self.frecency_file = frecency_file or Path.home() / ".groknroll" / "frecency.json"
        self.frecency_file.parent.mkdir(parents=True, exist_ok=True)

        self.file_search = FuzzyFileSearch(str(self.workspace_root))
        self.frecency_records: dict[str, FrecencyRecord] = {}
        self.mcp_resources: list[tuple[str, str]] = []  # (name, description)

        self._load_frecency()

    def _load_frecency(self):
        """Load frecency records from file"""
        if not self.frecency_file.exists():
            return

        try:
            with open(self.frecency_file) as f:
                data = json.load(f)

            for path, record_data in data.items():
                record = FrecencyRecord(
                    path=path,
                    access_count=record_data.get("access_count", 0),
                    last_accessed=record_data.get("last_accessed", 0.0),
                )
                record.update_score()
                self.frecency_records[path] = record
        except Exception:
            # If file is corrupted, start fresh
            self.frecency_records = {}

    def _save_frecency(self):
        """Save frecency records to file"""
        try:
            data = {
                path: {
                    "access_count": record.access_count,
                    "last_accessed": record.last_accessed,
                }
                for path, record in self.frecency_records.items()
            }

            with open(self.frecency_file, "w") as f:
                json.dump(data, f, indent=2)
        except Exception:
            pass

    def record_access(self, file_path: str):
        """
        Record file access for frecency tracking

        Args:
            file_path: Path to file that was accessed
        """
        # Normalize path
        path = str(Path(file_path).resolve())

        if path not in self.frecency_records:
            self.frecency_records[path] = FrecencyRecord(path=path)

        self.frecency_records[path].record_access()
        self._save_frecency()

    def get_frecency_score(self, file_path: str) -> float:
        """
        Get frecency score for a file

        Args:
            file_path: Path to file

        Returns:
            Frecency score (0-100)
        """
        path = str(Path(file_path).resolve())
        if path in self.frecency_records:
            return self.frecency_records[path].total_score
        return 0.0

    def should_trigger(self, text: str) -> bool:
        """
        Check if autocomplete should trigger

        Args:
            text: Current input text

        Returns:
            True if @ trigger detected
        """
        return "@" in text

    def detect_trigger_type(self, query: str) -> Optional[AutocompleteType]:
        """
        Detect what type of autocomplete is needed

        Args:
            query: Query string (e.g., "@main", "@agent:", "@mcp:")

        Returns:
            AutocompleteType or None
        """
        if not query.startswith("@"):
            return None

        query_lower = query.lower()

        if "@agent:" in query_lower or query == "@agent":
            return AutocompleteType.AGENT
        elif "@mcp:" in query_lower:
            return AutocompleteType.MCP_RESOURCE
        elif "#" in query:
            return AutocompleteType.LINE_RANGE
        else:
            return AutocompleteType.FILE

    def get_suggestions(
        self,
        query: str,
        max_results: int = 10,
        threshold: int = 60,
    ) -> list[AutocompleteSuggestion]:
        """
        Get autocomplete suggestions

        Args:
            query: Query string (e.g., "@main", "@agent:", "@mcp:fs")
            max_results: Maximum number of suggestions
            threshold: Fuzzy match threshold (0-100)

        Returns:
            List of suggestions sorted by frecency score
        """
        if not query.startswith("@"):
            return []

        trigger_type = self.detect_trigger_type(query)

        if trigger_type == AutocompleteType.AGENT:
            return self._get_agent_suggestions(query)
        elif trigger_type == AutocompleteType.MCP_RESOURCE:
            return self._get_mcp_suggestions(query)
        elif trigger_type == AutocompleteType.LINE_RANGE:
            return self._get_line_range_suggestions(query)
        else:
            return self._get_file_suggestions(query, max_results, threshold)

    def _get_file_suggestions(
        self,
        query: str,
        max_results: int,
        threshold: int,
    ) -> list[AutocompleteSuggestion]:
        """Get file suggestions with frecency scoring"""
        # Remove @ prefix for fuzzy search
        search_query = query[1:] if query.startswith("@") else query

        # Get fuzzy matches
        matches = self.file_search.search(search_query, limit=max_results * 2)

        suggestions = []
        for match in matches:
            # Get frecency score
            frecency_score = self.get_frecency_score(match.path)

            # Combine fuzzy score with frecency
            # Fuzzy score (0-100) + frecency boost (0-50)
            total_score = match.score + (frecency_score * 0.5)

            suggestions.append(AutocompleteSuggestion(
                text=f"@{match.path}",
                display=Path(match.path).name,
                type=AutocompleteType.FILE,
                score=total_score,
                description=str(Path(match.path).parent),
                metadata={"full_path": match.path},
            ))

        # Sort by score and limit
        suggestions.sort()
        return suggestions[:max_results]

    def _get_agent_suggestions(self, query: str) -> list[AutocompleteSuggestion]:
        """Get agent type suggestions"""
        # Remove @agent: prefix if present
        search_term = query.replace("@agent:", "").replace("@agent", "").strip().lower()

        suggestions = []
        for agent_name, description in self.AGENT_TYPES:
            # Simple substring match
            if not search_term or search_term in agent_name.lower():
                score = 100.0 if not search_term else 80.0
                suggestions.append(AutocompleteSuggestion(
                    text=f"@agent:{agent_name}",
                    display=agent_name,
                    type=AutocompleteType.AGENT,
                    score=score,
                    description=description,
                ))

        suggestions.sort()
        return suggestions

    def _get_mcp_suggestions(self, query: str) -> list[AutocompleteSuggestion]:
        """Get MCP resource suggestions"""
        # Remove @mcp: prefix
        search_term = query.replace("@mcp:", "").strip().lower()

        suggestions = []
        for resource_name, description in self.mcp_resources:
            if not search_term or search_term in resource_name.lower():
                score = 100.0 if not search_term else 80.0
                suggestions.append(AutocompleteSuggestion(
                    text=f"@mcp:{resource_name}",
                    display=resource_name,
                    type=AutocompleteType.MCP_RESOURCE,
                    score=score,
                    description=description,
                ))

        suggestions.sort()
        return suggestions

    def _get_line_range_suggestions(self, query: str) -> list[AutocompleteSuggestion]:
        """Get line range suggestions for @file#"""
        # Parse @file#range
        if "#" not in query:
            return []

        file_part, range_part = query.split("#", 1)
        file_path = file_part[1:] if file_part.startswith("@") else file_part

        # Check if file exists
        full_path = self.workspace_root / file_path
        if not full_path.exists() or not full_path.is_file():
            return []

        # Get file line count
        try:
            with open(full_path) as f:
                line_count = sum(1 for _ in f)
        except Exception:
            return []

        # Generate line range suggestions
        suggestions = []
        ranges = [
            (1, 20, "First 20 lines"),
            (1, 50, "First 50 lines"),
            (1, 100, "First 100 lines"),
            (line_count - 20, line_count, "Last 20 lines"),
            (line_count - 50, line_count, "Last 50 lines"),
        ]

        for start, end, description in ranges:
            if start < 1:
                start = 1
            if end > line_count:
                end = line_count
            if start >= end:
                continue

            suggestions.append(AutocompleteSuggestion(
                text=f"@{file_path}#{start}-{end}",
                display=f"#{start}-{end}",
                type=AutocompleteType.LINE_RANGE,
                score=100.0,
                description=description,
                metadata={"file": file_path, "start": start, "end": end},
            ))

        return suggestions

    def register_mcp_resource(self, name: str, description: str = ""):
        """
        Register an MCP resource for autocomplete

        Args:
            name: Resource name (e.g., "filesystem", "database")
            description: Human-readable description
        """
        self.mcp_resources.append((name, description))

    def clear_mcp_resources(self):
        """Clear all registered MCP resources"""
        self.mcp_resources.clear()

    def get_frecency_stats(self) -> dict[str, FrecencyRecord]:
        """Get all frecency records (for debugging/stats)"""
        return dict(self.frecency_records)

    def clear_frecency(self):
        """Clear all frecency tracking"""
        self.frecency_records.clear()
        self._save_frecency()


# Convenience functions

def create_autocomplete_provider(
    workspace_root: Optional[Path] = None,
) -> AutocompleteProvider:
    """
    Create autocomplete provider with default settings

    Args:
        workspace_root: Workspace root directory

    Returns:
        Configured AutocompleteProvider
    """
    return AutocompleteProvider(workspace_root=workspace_root)


def parse_file_reference(text: str) -> Optional[tuple[str, Optional[tuple[int, int]]]]:
    """
    Parse @file#L reference into file path and line range

    Args:
        text: Text like "@main.py#10-20" or "@main.py"

    Returns:
        Tuple of (file_path, (start_line, end_line)) or (file_path, None)
    """
    if not text.startswith("@"):
        return None

    text = text[1:]  # Remove @

    if "#" in text:
        file_path, range_str = text.split("#", 1)

        # Parse line range
        if "-" in range_str:
            try:
                start, end = range_str.split("-", 1)
                return (file_path, (int(start), int(end)))
            except ValueError:
                return None
        else:
            # Single line
            try:
                line = int(range_str)
                return (file_path, (line, line))
            except ValueError:
                return None
    else:
        return (text, None)


def expand_file_references(text: str, provider: AutocompleteProvider) -> str:
    """
    Expand @file references to actual file content

    Args:
        text: Text containing @file references
        provider: Autocomplete provider

    Returns:
        Text with @file references expanded to file content
    """
    import re

    # Find all @file references
    pattern = r"@([^\s#]+)(?:#(\d+)-(\d+))?"
    matches = re.finditer(pattern, text)

    result = text
    for match in matches:
        file_path = match.group(1)
        start_line = int(match.group(2)) if match.group(2) else None
        end_line = int(match.group(3)) if match.group(3) else None

        # Read file content
        full_path = provider.workspace_root / file_path
        if not full_path.exists():
            continue

        try:
            with open(full_path) as f:
                lines = f.readlines()

            if start_line and end_line:
                # Extract line range (1-indexed)
                content = "".join(lines[start_line - 1:end_line])
            else:
                # Full file
                content = "".join(lines)

            # Replace reference with content
            reference = match.group(0)
            result = result.replace(reference, content)

            # Record access
            provider.record_access(file_path)
        except Exception:
            continue

    return result
