"""Matrix configuration loader utility.

Provides a typed dataclass for Matrix world configuration and a loader
that fetches settings from YAML with sensible defaults.
"""

import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

import yaml

logger = logging.getLogger(__name__)


@dataclass
class PointLight:
    """A single point light in the Matrix world."""

    color: str = "#FFFFFF"
    intensity: float = 1.0
    position: dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0})

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "PointLight":
        """Create a PointLight from a dictionary with defaults."""
        return cls(
            color=data.get("color", "#FFFFFF"),
            intensity=data.get("intensity", 1.0),
            position=data.get("position", {"x": 0, "y": 0, "z": 0}),
        )


def _default_point_lights_factory() -> list[PointLight]:
    """Factory function for default point lights."""
    return [
        PointLight(
            color="#FFAA55",  # Warm amber (Workshop)
            intensity=1.2,
            position={"x": 0, "y": 5, "z": 0},
        ),
        PointLight(
            color="#3B82F6",  # Cool blue (Matrix)
            intensity=0.8,
            position={"x": -5, "y": 3, "z": -5},
        ),
        PointLight(
            color="#A855F7",  # Purple accent
            intensity=0.6,
            position={"x": 5, "y": 3, "z": 5},
        ),
    ]


@dataclass
class LightingConfig:
    """Lighting configuration for the Matrix world."""

    ambient_color: str = "#FFAA55"  # Warm amber (Workshop warmth)
    ambient_intensity: float = 0.5
    point_lights: list[PointLight] = field(default_factory=_default_point_lights_factory)

    @classmethod
    def from_dict(cls, data: dict[str, Any] | None) -> "LightingConfig":
        """Create a LightingConfig from a dictionary with defaults."""
        if data is None:
            data = {}

        point_lights_data = data.get("point_lights", [])
        point_lights = (
            [PointLight.from_dict(pl) for pl in point_lights_data]
            if point_lights_data
            else _default_point_lights_factory()
        )

        return cls(
            ambient_color=data.get("ambient_color", "#FFAA55"),
            ambient_intensity=data.get("ambient_intensity", 0.5),
            point_lights=point_lights,
        )


@dataclass
class EnvironmentConfig:
    """Environment settings for the Matrix world."""

    rain_enabled: bool = False
    starfield_enabled: bool = True
    fog_color: str = "#0f0f23"
    fog_density: float = 0.02

    @classmethod
    def from_dict(cls, data: dict[str, Any] | None) -> "EnvironmentConfig":
        """Create an EnvironmentConfig from a dictionary with defaults."""
        if data is None:
            data = {}
        return cls(
            rain_enabled=data.get("rain_enabled", False),
            starfield_enabled=data.get("starfield_enabled", True),
            fog_color=data.get("fog_color", "#0f0f23"),
            fog_density=data.get("fog_density", 0.02),
        )


@dataclass
class FeaturesConfig:
    """Feature toggles for the Matrix world."""

    chat_enabled: bool = True
    visitor_avatars: bool = True
    pip_familiar: bool = True
    workshop_portal: bool = True

    @classmethod
    def from_dict(cls, data: dict[str, Any] | None) -> "FeaturesConfig":
        """Create a FeaturesConfig from a dictionary with defaults."""
        if data is None:
            data = {}
        return cls(
            chat_enabled=data.get("chat_enabled", True),
            visitor_avatars=data.get("visitor_avatars", True),
            pip_familiar=data.get("pip_familiar", True),
            workshop_portal=data.get("workshop_portal", True),
        )


@dataclass
class AgentConfig:
    """Configuration for a single Matrix agent."""

    name: str = ""
    role: str = ""
    enabled: bool = True

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "AgentConfig":
        """Create an AgentConfig from a dictionary with defaults."""
        return cls(
            name=data.get("name", ""),
            role=data.get("role", ""),
            enabled=data.get("enabled", True),
        )


@dataclass
class AgentsConfig:
    """Agent registry configuration."""

    default_count: int = 5
    max_count: int = 20
    agents: list[AgentConfig] = field(default_factory=list)

    @classmethod
    def from_dict(cls, data: dict[str, Any] | None) -> "AgentsConfig":
        """Create an AgentsConfig from a dictionary with defaults."""
        if data is None:
            data = {}

        agents_data = data.get("agents", [])
        agents = [AgentConfig.from_dict(a) for a in agents_data] if agents_data else []

        return cls(
            default_count=data.get("default_count", 5),
            max_count=data.get("max_count", 20),
            agents=agents,
        )


@dataclass
class MatrixConfig:
    """Complete Matrix world configuration.

    Combines lighting, environment, features, and agent settings
    into a single configuration object.
    """

    lighting: LightingConfig = field(default_factory=LightingConfig)
    environment: EnvironmentConfig = field(default_factory=EnvironmentConfig)
    features: FeaturesConfig = field(default_factory=FeaturesConfig)
    agents: AgentsConfig = field(default_factory=AgentsConfig)

    @classmethod
    def from_dict(cls, data: dict[str, Any] | None) -> "MatrixConfig":
        """Create a MatrixConfig from a dictionary with defaults for missing sections."""
        if data is None:
            data = {}
        return cls(
            lighting=LightingConfig.from_dict(data.get("lighting")),
            environment=EnvironmentConfig.from_dict(data.get("environment")),
            features=FeaturesConfig.from_dict(data.get("features")),
            agents=AgentsConfig.from_dict(data.get("agents")),
        )

    def to_dict(self) -> dict[str, Any]:
        """Convert the configuration to a plain dictionary."""
        return {
            "lighting": {
                "ambient_color": self.lighting.ambient_color,
                "ambient_intensity": self.lighting.ambient_intensity,
                "point_lights": [
                    {
                        "color": pl.color,
                        "intensity": pl.intensity,
                        "position": pl.position,
                    }
                    for pl in self.lighting.point_lights
                ],
            },
            "environment": {
                "rain_enabled": self.environment.rain_enabled,
                "starfield_enabled": self.environment.starfield_enabled,
                "fog_color": self.environment.fog_color,
                "fog_density": self.environment.fog_density,
            },
            "features": {
                "chat_enabled": self.features.chat_enabled,
                "visitor_avatars": self.features.visitor_avatars,
                "pip_familiar": self.features.pip_familiar,
                "workshop_portal": self.features.workshop_portal,
            },
            "agents": {
                "default_count": self.agents.default_count,
                "max_count": self.agents.max_count,
                "agents": [
                    {"name": a.name, "role": a.role, "enabled": a.enabled}
                    for a in self.agents.agents
                ],
            },
        }


def load_from_yaml(path: str | Path) -> MatrixConfig:
    """Load Matrix configuration from a YAML file.

    Missing keys are filled with sensible defaults. If the file
    cannot be read or parsed, returns a fully default configuration.

    Args:
        path: Path to the YAML configuration file.

    Returns:
        A MatrixConfig instance with loaded or default values.
    """
    path = Path(path)

    if not path.exists():
        logger.warning("Matrix config file not found: %s, using defaults", path)
        return MatrixConfig()

    try:
        with open(path, encoding="utf-8") as f:
            raw_data = yaml.safe_load(f)

        if not isinstance(raw_data, dict):
            logger.warning("Matrix config invalid format, using defaults")
            return MatrixConfig()

        return MatrixConfig.from_dict(raw_data)

    except yaml.YAMLError as exc:
        logger.warning("Matrix config YAML parse error: %s, using defaults", exc)
        return MatrixConfig()
    except OSError as exc:
        logger.warning("Matrix config read error: %s, using defaults", exc)
        return MatrixConfig()
