"""
Tailwind Token Source

Extracts design tokens from Tailwind CSS configuration files.
Supports tailwind.config.js/ts and CSS-based Tailwind v4 configurations.
"""

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


class TailwindTokenSource(TokenSource):
    """
    Extract tokens from Tailwind CSS configuration.

    Parses:
    - tailwind.config.js/ts (theme and extend sections)
    - Tailwind v4 CSS-based configuration
    - CSS custom properties from Tailwind output
    """

    # Tailwind category mappings
    TAILWIND_CATEGORIES = {
        'colors': TokenCategory.COLORS,
        'backgroundColor': TokenCategory.COLORS,
        'textColor': TokenCategory.COLORS,
        'borderColor': TokenCategory.COLORS,
        'spacing': TokenCategory.SPACING,
        'padding': TokenCategory.SPACING,
        'margin': TokenCategory.SPACING,
        'gap': TokenCategory.SPACING,
        'fontSize': TokenCategory.TYPOGRAPHY,
        'fontFamily': TokenCategory.TYPOGRAPHY,
        'fontWeight': TokenCategory.TYPOGRAPHY,
        'lineHeight': TokenCategory.TYPOGRAPHY,
        'letterSpacing': TokenCategory.TYPOGRAPHY,
        'width': TokenCategory.SIZING,
        'height': TokenCategory.SIZING,
        'maxWidth': TokenCategory.SIZING,
        'maxHeight': TokenCategory.SIZING,
        'minWidth': TokenCategory.SIZING,
        'minHeight': TokenCategory.SIZING,
        'borderRadius': TokenCategory.BORDERS,
        'borderWidth': TokenCategory.BORDERS,
        'boxShadow': TokenCategory.SHADOWS,
        'dropShadow': TokenCategory.SHADOWS,
        'opacity': TokenCategory.OPACITY,
        'zIndex': TokenCategory.Z_INDEX,
        'transitionDuration': TokenCategory.MOTION,
        'transitionTimingFunction': TokenCategory.MOTION,
        'animation': TokenCategory.MOTION,
        'screens': TokenCategory.BREAKPOINTS,
    }

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

    async def extract(self, source: str) -> TokenCollection:
        """
        Extract tokens from Tailwind config.

        Args:
            source: Path to tailwind.config.js/ts or directory containing it

        Returns:
            TokenCollection with extracted tokens
        """
        config_path = self._find_config(source)
        if not config_path:
            raise FileNotFoundError(f"Tailwind config not found in: {source}")

        content = config_path.read_text(encoding="utf-8")
        source_file = str(config_path.absolute())

        # Parse based on file type
        if config_path.suffix in ('.js', '.cjs', '.mjs', '.ts'):
            tokens = self._parse_js_config(content, source_file)
        elif config_path.suffix == '.css':
            tokens = self._parse_css_config(content, source_file)
        else:
            tokens = []

        return TokenCollection(
            tokens=tokens,
            name=f"Tailwind Tokens from {config_path.name}",
            sources=[self._create_source_id(source_file)],
        )

    def _find_config(self, source: str) -> Optional[Path]:
        """Find Tailwind config file."""
        path = Path(source)

        # If it's a file, use it directly
        if path.is_file():
            return path

        # If it's a directory, look for config files
        if path.is_dir():
            config_names = [
                'tailwind.config.js',
                'tailwind.config.cjs',
                'tailwind.config.mjs',
                'tailwind.config.ts',
            ]
            for name in config_names:
                config_path = path / name
                if config_path.exists():
                    return config_path

        return None

    def _parse_js_config(self, content: str, source_file: str) -> List[DesignToken]:
        """Parse JavaScript/TypeScript Tailwind config."""
        tokens = []

        # Extract theme object using regex (simplified parsing)
        # This handles common patterns but may not cover all edge cases

        # Look for theme: { ... } or theme.extend: { ... }
        theme_match = re.search(
            r'theme\s*:\s*\{([\s\S]*?)\n\s*\}(?=\s*[,}])',
            content
        )

        extend_match = re.search(
            r'extend\s*:\s*\{([\s\S]*?)\n\s{4}\}',
            content
        )

        if extend_match:
            theme_content = extend_match.group(1)
            tokens.extend(self._parse_theme_object(theme_content, source_file, "extend"))

        if theme_match and not extend_match:
            theme_content = theme_match.group(1)
            tokens.extend(self._parse_theme_object(theme_content, source_file, "theme"))

        return tokens

    def _parse_theme_object(self, content: str, source_file: str, prefix: str) -> List[DesignToken]:
        """Parse theme object content."""
        tokens = []

        # Find property blocks like: colors: { primary: '#3B82F6', ... }
        prop_pattern = re.compile(
            r"(\w+)\s*:\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}",
            re.MULTILINE
        )

        for match in prop_pattern.finditer(content):
            category_name = match.group(1)
            category_content = match.group(2)

            category = self.TAILWIND_CATEGORIES.get(
                category_name, TokenCategory.OTHER
            )

            # Parse values in this category
            tokens.extend(
                self._parse_category_values(
                    category_name,
                    category_content,
                    source_file,
                    category
                )
            )

        return tokens

    def _parse_category_values(
        self,
        category_name: str,
        content: str,
        source_file: str,
        category: TokenCategory
    ) -> List[DesignToken]:
        """Parse values within a category."""
        tokens = []

        # Match key: value pairs
        # Handles: key: 'value', key: "value", key: value, 'key': value
        value_pattern = re.compile(
            r"['\"]?(\w[\w-]*)['\"]?\s*:\s*['\"]?([^,'\"}\n]+)['\"]?",
        )

        for match in value_pattern.finditer(content):
            key = match.group(1)
            value = match.group(2).strip()

            # Skip function calls and complex values for now
            if '(' in value or '{' in value:
                continue

            # Skip references to other values
            if value.startswith('colors.') or value.startswith('theme('):
                continue

            token = DesignToken(
                name=f"{category_name}.{key}",
                value=value,
                source=self._create_source_id(source_file),
                source_file=source_file,
                original_name=f"{category_name}.{key}",
                original_value=value,
                category=category,
            )
            token.tags.append("tailwind")
            tokens.append(token)

        return tokens

    def _parse_css_config(self, content: str, source_file: str) -> List[DesignToken]:
        """Parse Tailwind v4 CSS-based configuration."""
        tokens = []

        # Tailwind v4 uses @theme directive
        theme_match = re.search(
            r'@theme\s*\{([\s\S]*?)\}',
            content
        )

        if theme_match:
            theme_content = theme_match.group(1)

            # Parse CSS custom properties
            var_pattern = re.compile(
                r'(--[\w-]+)\s*:\s*([^;]+);'
            )

            for match in var_pattern.finditer(theme_content):
                var_name = match.group(1)
                var_value = match.group(2).strip()

                # Determine category from variable name
                category = self._category_from_var_name(var_name)

                token = DesignToken(
                    name=self._normalize_var_name(var_name),
                    value=var_value,
                    source=self._create_source_id(source_file),
                    source_file=source_file,
                    original_name=var_name,
                    original_value=var_value,
                    category=category,
                )
                token.tags.append("tailwind-v4")
                tokens.append(token)

        return tokens

    def _normalize_var_name(self, var_name: str) -> str:
        """Convert CSS variable name to token name."""
        name = var_name.lstrip('-')
        name = name.replace('-', '.')
        return name.lower()

    def _category_from_var_name(self, var_name: str) -> TokenCategory:
        """Determine category from variable name."""
        name_lower = var_name.lower()

        if 'color' in name_lower or 'bg' in name_lower:
            return TokenCategory.COLORS
        if 'spacing' in name_lower or 'gap' in name_lower:
            return TokenCategory.SPACING
        if 'font' in name_lower or 'text' in name_lower:
            return TokenCategory.TYPOGRAPHY
        if 'radius' in name_lower or 'border' in name_lower:
            return TokenCategory.BORDERS
        if 'shadow' in name_lower:
            return TokenCategory.SHADOWS

        return TokenCategory.OTHER


class TailwindClassExtractor:
    """
    Extract Tailwind class usage from source files.

    Identifies Tailwind utility classes for analysis and migration.
    """

    # Common Tailwind class prefixes
    TAILWIND_PREFIXES = [
        'bg-', 'text-', 'border-', 'ring-',
        'p-', 'px-', 'py-', 'pt-', 'pr-', 'pb-', 'pl-',
        'm-', 'mx-', 'my-', 'mt-', 'mr-', 'mb-', 'ml-',
        'w-', 'h-', 'min-w-', 'min-h-', 'max-w-', 'max-h-',
        'flex-', 'grid-', 'gap-',
        'font-', 'text-', 'leading-', 'tracking-',
        'rounded-', 'shadow-', 'opacity-',
        'z-', 'transition-', 'duration-', 'ease-',
    ]

    async def extract_usage(self, source: str) -> Dict[str, List[str]]:
        """
        Extract Tailwind class usage from file.

        Returns dict mapping class categories to list of used classes.
        """
        if Path(source).exists():
            content = Path(source).read_text(encoding="utf-8")
        else:
            content = source

        usage: Dict[str, List[str]] = {}

        # Find className or class attributes
        class_pattern = re.compile(
            r'(?:className|class)\s*=\s*["\']([^"\']+)["\']'
        )

        for match in class_pattern.finditer(content):
            classes = match.group(1).split()

            for cls in classes:
                # Check if it's a Tailwind class
                for prefix in self.TAILWIND_PREFIXES:
                    if cls.startswith(prefix):
                        category = prefix.rstrip('-')
                        if category not in usage:
                            usage[category] = []
                        if cls not in usage[category]:
                            usage[category].append(cls)
                        break

        return usage
