"""
Storybook Theme Generator

Generates Storybook theme configurations from design tokens.
"""

import json
from pathlib import Path
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field


@dataclass
class StorybookTheme:
    """Storybook theme configuration."""
    name: str = "dss-theme"
    base: str = "light"  # 'light' or 'dark'

    # Brand
    brand_title: str = "Design System"
    brand_url: str = ""
    brand_image: str = ""
    brand_target: str = "_self"

    # Colors
    color_primary: str = "#3B82F6"
    color_secondary: str = "#10B981"

    # UI Colors
    app_bg: str = "#FFFFFF"
    app_content_bg: str = "#FFFFFF"
    app_border_color: str = "#E5E7EB"

    # Text colors
    text_color: str = "#1F2937"
    text_inverse_color: str = "#FFFFFF"
    text_muted_color: str = "#6B7280"

    # Toolbar
    bar_text_color: str = "#6B7280"
    bar_selected_color: str = "#3B82F6"
    bar_bg: str = "#FFFFFF"

    # Form colors
    input_bg: str = "#FFFFFF"
    input_border: str = "#D1D5DB"
    input_text_color: str = "#1F2937"
    input_border_radius: int = 4

    # Typography
    font_base: str = '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
    font_code: str = '"Fira Code", "Monaco", monospace'

    def to_dict(self) -> Dict[str, Any]:
        return {
            "base": self.base,
            "brandTitle": self.brand_title,
            "brandUrl": self.brand_url,
            "brandImage": self.brand_image,
            "brandTarget": self.brand_target,
            "colorPrimary": self.color_primary,
            "colorSecondary": self.color_secondary,
            "appBg": self.app_bg,
            "appContentBg": self.app_content_bg,
            "appBorderColor": self.app_border_color,
            "textColor": self.text_color,
            "textInverseColor": self.text_inverse_color,
            "textMutedColor": self.text_muted_color,
            "barTextColor": self.bar_text_color,
            "barSelectedColor": self.bar_selected_color,
            "barBg": self.bar_bg,
            "inputBg": self.input_bg,
            "inputBorder": self.input_border,
            "inputTextColor": self.input_text_color,
            "inputBorderRadius": self.input_border_radius,
            "fontBase": self.font_base,
            "fontCode": self.font_code,
        }


class ThemeGenerator:
    """
    Generates Storybook theme configurations from design tokens.
    """

    def __init__(self, root_path: Optional[str] = None):
        """
        Initialize ThemeGenerator.

        Args:
            root_path: Optional project root path for finding tokens and writing output
        """
        self.root = Path(root_path).resolve() if root_path else Path.cwd()

    def generate(self, brand_title: str = "Design System", base: str = "light") -> Dict[str, Any]:
        """
        Generate Storybook theme configuration from project tokens.

        This is the main entry point for theme generation. It searches for design tokens
        in the project and generates Storybook theme configuration files.

        Args:
            brand_title: Brand title for Storybook
            base: Base theme ('light' or 'dark')

        Returns:
            Dict with generated theme configuration and files
        """
        # Look for tokens in common locations
        token_paths = [
            self.root / 'tokens' / 'tokens.json',
            self.root / 'design-tokens' / 'tokens.json',
            self.root / 'src' / 'tokens' / 'tokens.json',
            self.root / '.dss' / 'tokens.json',
            self.root / 'dss_output' / 'tokens.json',
            self.root / 'dss' / 'core_tokens' / 'tokens.json',  # DSS core tokens
        ]

        tokens = []
        token_source = None

        for token_path in token_paths:
            if token_path.exists():
                try:
                    token_data = json.loads(token_path.read_text())
                    if isinstance(token_data, list):
                        tokens = token_data
                    elif isinstance(token_data, dict):
                        # Flatten nested token structure
                        tokens = self._flatten_tokens(token_data)
                    token_source = str(token_path)
                    break
                except (json.JSONDecodeError, IOError):
                    continue

        # Generate theme from tokens (or use defaults if no tokens found)
        theme = self.generate_from_tokens(tokens, brand_title, base)

        # Determine output directory for Storybook config
        storybook_dir = self.root / '.storybook'
        output_dir = str(storybook_dir) if storybook_dir.exists() else None

        # Generate configuration files
        files = self.generate_full_config(tokens, brand_title, output_dir)

        return {
            "theme": theme.to_dict(),
            "files_generated": list(files.keys()),
            "token_source": token_source,
            "tokens_found": len(tokens),
            "output_directory": output_dir,
            "written": output_dir is not None,
        }

    def _flatten_tokens(self, token_dict: Dict[str, Any], prefix: str = "") -> List[Dict[str, Any]]:
        """Flatten nested token dictionary to list of {name, value} dicts."""
        tokens = []
        for key, value in token_dict.items():
            name = f"{prefix}.{key}" if prefix else key
            if isinstance(value, dict):
                if "value" in value:
                    # This is a token leaf
                    tokens.append({"name": name, "value": value["value"]})
                else:
                    # Recurse into nested structure
                    tokens.extend(self._flatten_tokens(value, name))
            elif isinstance(value, str):
                tokens.append({"name": name, "value": value})
        return tokens

    # Token name mappings to Storybook theme properties
    TOKEN_MAPPINGS = {
        # Primary/Secondary
        "color.primary.500": "color_primary",
        "color.primary.600": "color_primary",
        "color.secondary.500": "color_secondary",
        "color.accent.500": "color_secondary",

        # Backgrounds
        "color.neutral.50": "app_bg",
        "color.background": "app_bg",
        "color.surface": "app_content_bg",

        # Borders
        "color.neutral.200": "app_border_color",
        "color.border": "app_border_color",

        # Text
        "color.neutral.900": "text_color",
        "color.neutral.800": "text_color",
        "color.foreground": "text_color",
        "color.neutral.500": "text_muted_color",
        "color.muted": "text_muted_color",

        # Input
        "color.neutral.300": "input_border",
        "radius.md": "input_border_radius",
    }

    def generate_from_tokens(
        self,
        tokens: List[Dict[str, Any]],
        brand_title: str = "Design System",
        base: str = "light",
    ) -> StorybookTheme:
        """
        Generate Storybook theme from design tokens.

        Args:
            tokens: List of token dicts with 'name' and 'value'
            brand_title: Brand title for Storybook
            base: Base theme ('light' or 'dark')

        Returns:
            StorybookTheme configured from tokens
        """
        theme = StorybookTheme(
            name="dss-theme",
            base=base,
            brand_title=brand_title,
        )

        # Map tokens to theme properties
        for token in tokens:
            name = token.get("name", "")
            value = token.get("value", "")

            # Skip non-string values (complex tokens)
            if not isinstance(value, str):
                continue

            # Check direct mappings
            if name in self.TOKEN_MAPPINGS:
                prop = self.TOKEN_MAPPINGS[name]
                setattr(theme, prop, value)
                continue

            # Check partial matches
            name_lower = name.lower()

            if "primary" in name_lower and "500" in name_lower:
                theme.color_primary = value
            elif "secondary" in name_lower and "500" in name_lower:
                theme.color_secondary = value
            elif "background" in name_lower and self._is_light_color(value):
                theme.app_bg = value
            elif "foreground" in name_lower or ("text" in name_lower and "color" in name_lower):
                theme.text_color = value

        # Adjust for dark mode
        if base == "dark":
            theme = self._adjust_for_dark_mode(theme)

        return theme

    def _is_light_color(self, value: Any) -> bool:
        """Check if a color value is light (for background suitability)."""
        # Handle non-string values (dicts, etc.)
        if not isinstance(value, str):
            return True  # Assume light if not a string

        if not value.startswith("#"):
            return True  # Assume light if not hex

        # Parse hex color
        hex_color = value.lstrip("#")
        if len(hex_color) == 3:
            hex_color = "".join(c * 2 for c in hex_color)

        try:
            r = int(hex_color[0:2], 16)
            g = int(hex_color[2:4], 16)
            b = int(hex_color[4:6], 16)
            # Calculate luminance
            luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
            return luminance > 0.5
        except (ValueError, IndexError):
            return True

    def _adjust_for_dark_mode(self, theme: StorybookTheme) -> StorybookTheme:
        """Adjust theme for dark mode if colors aren't already dark."""
        # Swap light/dark if needed
        if self._is_light_color(theme.app_bg):
            theme.app_bg = "#1F2937"
            theme.app_content_bg = "#111827"
            theme.app_border_color = "#374151"
            theme.text_color = "#F9FAFB"
            theme.text_muted_color = "#9CA3AF"
            theme.bar_bg = "#1F2937"
            theme.bar_text_color = "#9CA3AF"
            theme.input_bg = "#374151"
            theme.input_border = "#4B5563"
            theme.input_text_color = "#F9FAFB"

        return theme

    def generate_theme_file(
        self,
        theme: StorybookTheme,
        format: str = "ts",
    ) -> str:
        """
        Generate Storybook theme file content.

        Args:
            theme: StorybookTheme to export
            format: Output format ('ts', 'js', 'json')

        Returns:
            Theme file content as string
        """
        if format == "json":
            return json.dumps(theme.to_dict(), indent=2)

        theme_dict = theme.to_dict()

        if format == "ts":
            lines = [
                "import { create } from '@storybook/theming/create';",
                "",
                "export const dssTheme = create({",
            ]
        else:  # js
            lines = [
                "const { create } = require('@storybook/theming/create');",
                "",
                "module.exports = create({",
            ]

        for key, value in theme_dict.items():
            if isinstance(value, str):
                lines.append(f"  {key}: '{value}',")
            else:
                lines.append(f"  {key}: {value},")

        lines.extend([
            "});",
            "",
        ])

        return "\n".join(lines)

    def generate_manager_file(self, theme_import: str = "./dss-theme") -> str:
        """
        Generate Storybook manager.ts file.

        Args:
            theme_import: Import path for theme

        Returns:
            Manager file content
        """
        return f"""import {{ addons }} from '@storybook/manager-api';
import {{ dssTheme }} from '{theme_import}';

addons.setConfig({{
  theme: dssTheme,
}});
"""

    def generate_preview_file(
        self,
        tokens: List[Dict[str, Any]],
        include_css_vars: bool = True,
    ) -> str:
        """
        Generate Storybook preview.ts file with token CSS variables.

        Args:
            tokens: List of token dicts
            include_css_vars: Include CSS variable injection

        Returns:
            Preview file content
        """
        lines = [
            "import type { Preview } from '@storybook/react';",
            "",
        ]

        if include_css_vars:
            # Generate CSS variables from tokens
            css_vars = []
            for token in tokens:
                name = token.get("name", "").replace(".", "-")
                value = token.get("value", "")
                css_vars.append(f"  --{name}: {value};")

            lines.extend([
                "// Inject design tokens as CSS variables",
                "const tokenStyles = `",
                ":root {",
            ])
            lines.extend(css_vars)
            lines.extend([
                "}",
                "`;",
                "",
                "// Add styles to document",
                "const styleSheet = document.createElement('style');",
                "styleSheet.textContent = tokenStyles;",
                "document.head.appendChild(styleSheet);",
                "",
            ])

        lines.extend([
            "const preview: Preview = {",
            "  parameters: {",
            "    controls: {",
            "      matchers: {",
            "        color: /(background|color)$/i,",
            "        date: /Date$/i,",
            "      },",
            "    },",
            "    backgrounds: {",
            "      default: 'light',",
            "      values: [",
            "        { name: 'light', value: '#FFFFFF' },",
            "        { name: 'dark', value: '#1F2937' },",
            "      ],",
            "    },",
            "  },",
            "};",
            "",
            "export default preview;",
        ])

        return "\n".join(lines)

    def generate_full_config(
        self,
        tokens: List[Dict[str, Any]],
        brand_title: str = "Design System",
        output_dir: Optional[str] = None,
    ) -> Dict[str, str]:
        """
        Generate complete Storybook configuration files.

        Args:
            tokens: List of token dicts
            brand_title: Brand title
            output_dir: Optional directory to write files

        Returns:
            Dict mapping filenames to content
        """
        # Generate theme
        theme = self.generate_from_tokens(tokens, brand_title)

        files = {
            "dss-theme.ts": self.generate_theme_file(theme, "ts"),
            "manager.ts": self.generate_manager_file(),
            "preview.ts": self.generate_preview_file(tokens),
        }

        # Write files if output_dir provided
        if output_dir:
            out_path = Path(output_dir)
            out_path.mkdir(parents=True, exist_ok=True)

            for filename, content in files.items():
                (out_path / filename).write_text(content)

        return files
