"""
SCSS Token Source

Extracts design tokens from SCSS/Sass variables.
Supports $variable declarations and @use module variables.
"""

import re
from pathlib import Path
from typing import List, Dict, Optional
from .base import DesignToken, TokenCollection, TokenSource


class SCSSTokenSource(TokenSource):
    """
    Extract tokens from SCSS/Sass files.

    Parses:
    - $variable: value;
    - $variable: value !default;
    - // Comment descriptions
    - @use module variables
    - Maps: $colors: (primary: #3B82F6, secondary: #10B981);
    """

    @property
    def source_type(self) -> str:
        return "scss"

    async def extract(self, source: str) -> TokenCollection:
        """
        Extract tokens from SCSS file or content.

        Args:
            source: File path or SCSS content string

        Returns:
            TokenCollection with extracted tokens
        """
        if self._is_file_path(source):
            file_path = Path(source)
            if not file_path.exists():
                raise FileNotFoundError(f"SCSS file not found: {source}")
            content = file_path.read_text(encoding="utf-8")
            source_file = str(file_path.absolute())
        else:
            content = source
            source_file = "<inline>"

        tokens = []

        # Extract simple variables
        tokens.extend(self._parse_variables(content, source_file))

        # Extract map variables
        tokens.extend(self._parse_maps(content, source_file))

        return TokenCollection(
            tokens=tokens,
            name=f"SCSS Tokens from {Path(source_file).name if source_file != '<inline>' else 'inline'}",
            sources=[self._create_source_id(source_file)],
        )

    def _is_file_path(self, source: str) -> bool:
        """Check if source looks like a file path."""
        if '$' in source and ':' in source:
            return False
        if source.endswith('.scss') or source.endswith('.sass'):
            return True
        return Path(source).exists()

    def _parse_variables(self, content: str, source_file: str) -> List[DesignToken]:
        """Parse simple $variable declarations."""
        tokens = []
        lines = content.split('\n')

        # Pattern for variable declarations
        var_pattern = re.compile(
            r'^\s*'
            r'(\$[\w-]+)\s*:\s*'  # Variable name
            r'([^;!]+)'           # Value
            r'(\s*!default)?'     # Optional !default
            r'\s*;',
            re.MULTILINE
        )

        # Track comments for descriptions
        prev_comment = ""

        for i, line in enumerate(lines, 1):
            # Check for comment
            comment_match = re.match(r'^\s*//\s*(.+)$', line)
            if comment_match:
                prev_comment = comment_match.group(1).strip()
                continue

            # Check for variable
            var_match = var_pattern.match(line)
            if var_match:
                var_name = var_match.group(1)
                var_value = var_match.group(2).strip()
                is_default = bool(var_match.group(3))

                # Skip if value is a map (handled separately)
                if var_value.startswith('(') and var_value.endswith(')'):
                    prev_comment = ""
                    continue

                # Skip if value references another variable that we can't resolve
                if var_value.startswith('$') and '(' not in var_value:
                    # It's a simple variable reference, try to extract
                    pass

                token = DesignToken(
                    name=self._normalize_var_name(var_name),
                    value=self._process_value(var_value),
                    description=prev_comment,
                    source=self._create_source_id(source_file, i),
                    source_file=source_file,
                    source_line=i,
                    original_name=var_name,
                    original_value=var_value,
                )

                if is_default:
                    token.tags.append("default")

                tokens.append(token)
                prev_comment = ""
            else:
                # Reset comment if line doesn't match
                if line.strip() and not line.strip().startswith('//'):
                    prev_comment = ""

        return tokens

    def _parse_maps(self, content: str, source_file: str) -> List[DesignToken]:
        """Parse SCSS map declarations."""
        tokens = []

        # Pattern for map declarations (handles multi-line)
        map_pattern = re.compile(
            r'\$(\w[\w-]*)\s*:\s*\(([\s\S]*?)\)\s*;',
            re.MULTILINE
        )

        for match in map_pattern.finditer(content):
            map_name = match.group(1)
            map_content = match.group(2)

            # Get line number
            line_num = content[:match.start()].count('\n') + 1

            # Parse map entries
            entries = self._parse_map_entries(map_content)

            for key, value in entries.items():
                token = DesignToken(
                    name=f"{self._normalize_var_name('$' + map_name)}.{key}",
                    value=self._process_value(value),
                    source=self._create_source_id(source_file, line_num),
                    source_file=source_file,
                    source_line=line_num,
                    original_name=f"${map_name}.{key}",
                    original_value=value,
                )
                token.tags.append("from-map")
                tokens.append(token)

        return tokens

    def _parse_map_entries(self, map_content: str) -> Dict[str, str]:
        """Parse entries from a SCSS map."""
        entries = {}

        # Handle nested maps and simple key-value pairs
        # This is a simplified parser for common cases

        # Remove comments
        map_content = re.sub(r'//[^\n]*', '', map_content)

        # Split by comma (not inside parentheses)
        depth = 0
        current = ""
        parts = []

        for char in map_content:
            if char == '(':
                depth += 1
                current += char
            elif char == ')':
                depth -= 1
                current += char
            elif char == ',' and depth == 0:
                parts.append(current.strip())
                current = ""
            else:
                current += char

        if current.strip():
            parts.append(current.strip())

        # Parse each part
        for part in parts:
            if ':' in part:
                key, value = part.split(':', 1)
                key = key.strip().strip('"\'')
                value = value.strip()
                entries[key] = value

        return entries

    def _normalize_var_name(self, var_name: str) -> str:
        """Convert SCSS variable name to token name."""
        # Remove $ prefix
        name = var_name.lstrip('$')
        # Convert kebab-case and underscores to dots
        name = re.sub(r'[-_]', '.', name)
        return name.lower()

    def _process_value(self, value: str) -> str:
        """Process SCSS value for token storage."""
        value = value.strip()

        # Handle function calls (keep as-is for now)
        if '(' in value and ')' in value:
            return value

        # Handle quotes
        if (value.startswith('"') and value.endswith('"')) or \
           (value.startswith("'") and value.endswith("'")):
            return value[1:-1]

        return value


class SCSSVariableResolver:
    """
    Resolve SCSS variable references.

    Builds a dependency graph and resolves $var references to actual values.
    """

    def __init__(self):
        self.variables: Dict[str, str] = {}
        self.resolved: Dict[str, str] = {}

    def add_variable(self, name: str, value: str) -> None:
        """Add a variable to the resolver."""
        self.variables[name] = value

    def resolve(self, name: str) -> Optional[str]:
        """Resolve a variable to its final value."""
        if name in self.resolved:
            return self.resolved[name]

        value = self.variables.get(name)
        if not value:
            return None

        # Check if value references other variables
        if '$' in value:
            resolved_value = self._resolve_references(value)
            self.resolved[name] = resolved_value
            return resolved_value

        self.resolved[name] = value
        return value

    def _resolve_references(self, value: str, depth: int = 0) -> str:
        """Recursively resolve variable references in a value."""
        if depth > 10:  # Prevent infinite loops
            return value

        # Find variable references
        var_pattern = re.compile(r'\$[\w-]+')

        def replace_var(match):
            var_name = match.group(0)
            resolved = self.resolve(var_name.lstrip('$'))
            return resolved if resolved else var_name

        return var_pattern.sub(replace_var, value)

    def resolve_all(self) -> Dict[str, str]:
        """Resolve all variables."""
        for name in self.variables:
            self.resolve(name)
        return self.resolved
