"""Base classes and protocols for themes.

This module defines the core abstractions for the theming system.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field, replace
from typing import Any, Protocol, runtime_checkable


@dataclass(frozen=True)
class ThemeColors:
    """Color palette for a theme.

    All colors should be valid CSS color values.
    """
    # Main colors
    background: str = "#ffffff"
    surface: str = "#f8f9fa"
    text_primary: str = "#1a1a2e"
    text_secondary: str = "#6c757d"

    # Brand colors
    primary: str = "#2563eb"
    secondary: str = "#7209b7"
    accent: str = "#f72585"

    # Semantic colors
    success: str = "#10b981"
    warning: str = "#f59e0b"
    error: str = "#ef4444"
    info: str = "#3b82f6"

    # Border and shadow
    border: str = "#e5e7eb"
    shadow: str = "rgba(0, 0, 0, 0.05)"

    # Chart colors
    chart_palette: tuple[str, ...] = (
        "#2563eb", "#7209b7", "#f72585", "#4cc9f0",
        "#06d6a0", "#ffd166", "#ef476f", "#118ab2",
        "#073b4c", "#ff6b6b"
    )

    def to_css_vars(self) -> dict[str, str]:
        """Convert to CSS custom properties.

        Returns:
            Dictionary of CSS variable names to values.
        """
        vars_dict = {}
        for attr_name, value in self.__dict__.items():
            if attr_name == "chart_palette":
                for i, color in enumerate(value):
                    vars_dict[f"--chart-color-{i}"] = color
            else:
                css_name = attr_name.replace("_", "-")
                vars_dict[f"--color-{css_name}"] = value
        return vars_dict


@dataclass(frozen=True)
class ThemeTypography:
    """Typography settings for a theme."""
    font_family: str = "'Inter', system-ui, -apple-system, sans-serif"
    font_family_mono: str = "'JetBrains Mono', 'Fira Code', monospace"

    font_size_xs: str = "0.75rem"
    font_size_sm: str = "0.875rem"
    font_size_base: str = "1rem"
    font_size_lg: str = "1.125rem"
    font_size_xl: str = "1.25rem"
    font_size_2xl: str = "1.5rem"
    font_size_3xl: str = "2rem"

    font_weight_normal: int = 400
    font_weight_medium: int = 500
    font_weight_semibold: int = 600
    font_weight_bold: int = 700

    line_height_tight: float = 1.25
    line_height_normal: float = 1.5
    line_height_relaxed: float = 1.75

    def to_css_vars(self) -> dict[str, str]:
        """Convert to CSS custom properties."""
        vars_dict = {}
        for attr_name, value in self.__dict__.items():
            css_name = attr_name.replace("_", "-")
            vars_dict[f"--{css_name}"] = str(value)
        return vars_dict


@dataclass(frozen=True)
class ThemeSpacing:
    """Spacing and layout settings for a theme."""
    border_radius_sm: str = "4px"
    border_radius_md: str = "8px"
    border_radius_lg: str = "12px"
    border_radius_xl: str = "16px"

    spacing_xs: str = "0.25rem"
    spacing_sm: str = "0.5rem"
    spacing_md: str = "1rem"
    spacing_lg: str = "1.5rem"
    spacing_xl: str = "2rem"
    spacing_2xl: str = "3rem"

    shadow_sm: str = "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
    shadow_md: str = "0 4px 6px -1px rgba(0, 0, 0, 0.1)"
    shadow_lg: str = "0 10px 15px -3px rgba(0, 0, 0, 0.1)"
    shadow_xl: str = "0 20px 25px -5px rgba(0, 0, 0, 0.1)"

    def to_css_vars(self) -> dict[str, str]:
        """Convert to CSS custom properties."""
        vars_dict = {}
        for attr_name, value in self.__dict__.items():
            css_name = attr_name.replace("_", "-")
            vars_dict[f"--{css_name}"] = str(value)
        return vars_dict


@dataclass
class ThemeAssets:
    """Asset files for a theme (logo, favicon, etc.)."""
    logo_url: str | None = None
    logo_base64: str | None = None
    favicon_url: str | None = None
    favicon_base64: str | None = None
    custom_fonts: list[str] = field(default_factory=list)
    external_css: list[str] = field(default_factory=list)
    external_js: list[str] = field(default_factory=list)


@dataclass
class ThemeConfig:
    """Complete theme configuration."""
    name: str
    display_name: str = ""
    description: str = ""

    colors: ThemeColors = field(default_factory=ThemeColors)
    typography: ThemeTypography = field(default_factory=ThemeTypography)
    spacing: ThemeSpacing = field(default_factory=ThemeSpacing)
    assets: ThemeAssets = field(default_factory=ThemeAssets)

    # Custom CSS to append
    custom_css: str = ""

    # Text customization
    header_text: str = ""
    footer_text: str = "Generated by Truthound"

    # Layout options
    show_header: bool = True
    show_footer: bool = True
    show_toc: bool = True
    compact_mode: bool = False

    def __post_init__(self) -> None:
        if not self.display_name:
            self.display_name = self.name.replace("_", " ").title()

    def to_css_vars(self) -> str:
        """Generate CSS custom properties block.

        Returns:
            CSS :root block with all variables.
        """
        all_vars = {}
        all_vars.update(self.colors.to_css_vars())
        all_vars.update(self.typography.to_css_vars())
        all_vars.update(self.spacing.to_css_vars())

        lines = [":root {"]
        for name, value in all_vars.items():
            lines.append(f"    {name}: {value};")
        lines.append("}")

        return "\n".join(lines)


@runtime_checkable
class Theme(Protocol):
    """Protocol for theme implementations.

    Themes provide styling and branding for reports.
    """

    @property
    def name(self) -> str:
        """Get the theme name."""
        ...

    def get_css(self) -> str:
        """Get the complete CSS for this theme.

        Returns:
            CSS string including variables and styles.
        """
        ...

    def get_assets(self) -> dict[str, Any]:
        """Get theme assets (logo, fonts, etc.).

        Returns:
            Dictionary of asset names to values.
        """
        ...

    def customize(self, **overrides: Any) -> "Theme":
        """Create a new theme with customizations.

        Args:
            **overrides: Values to override.

        Returns:
            New theme instance with overrides applied.
        """
        ...


class BaseTheme(ABC):
    """Abstract base class for themes.

    Provides common functionality and CSS generation.
    """

    def __init__(self, config: ThemeConfig) -> None:
        """Initialize with configuration.

        Args:
            config: Theme configuration.
        """
        self._config = config

    @property
    def name(self) -> str:
        """Get the theme name."""
        return self._config.name

    @property
    def display_name(self) -> str:
        """Get the display name."""
        return self._config.display_name

    @property
    def config(self) -> ThemeConfig:
        """Get the theme configuration."""
        return self._config

    @property
    def colors(self) -> ThemeColors:
        """Get the color palette."""
        return self._config.colors

    @property
    def typography(self) -> ThemeTypography:
        """Get typography settings."""
        return self._config.typography

    @property
    def spacing(self) -> ThemeSpacing:
        """Get spacing settings."""
        return self._config.spacing

    @property
    def logo_url(self) -> str | None:
        """Get logo URL."""
        return self._config.assets.logo_url

    def to_css_vars(self) -> str:
        """Generate CSS custom properties block.

        Delegates to the underlying ThemeConfig.to_css_vars().

        Returns:
            CSS :root block with all variables.
        """
        return self._config.to_css_vars()

    def get_css(self) -> str:
        """Get the complete CSS for this theme.

        Returns:
            CSS string including variables and custom styles.
        """
        parts = [
            self._config.to_css_vars(),
            self._get_base_css(),
            self._config.custom_css,
        ]
        return "\n\n".join(p for p in parts if p)

    def get_assets(self) -> dict[str, Any]:
        """Get theme assets.

        Returns:
            Dictionary of asset names to values.
        """
        assets = self._config.assets
        return {
            "logo_url": assets.logo_url,
            "logo_base64": assets.logo_base64,
            "favicon_url": assets.favicon_url,
            "custom_fonts": assets.custom_fonts,
            "external_css": assets.external_css,
            "external_js": assets.external_js,
        }

    @abstractmethod
    def customize(self, **overrides: Any) -> "BaseTheme":
        """Create a new theme with customizations.

        Args:
            **overrides: Values to override.

        Returns:
            New theme instance.
        """
        pass

    def _get_base_css(self) -> str:
        """Get the base CSS for this theme.

        Can be overridden by subclasses for theme-specific styles.

        Returns:
            Base CSS string.
        """
        return ""

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(name={self.name!r})"
