"""AfsSimulationConfig — Centralized configuration for AFS simulations."""

from dataclasses import dataclass, field
from typing import Optional
import json


@dataclass
class EngineConfig:
    ticks_per_generation: int = 100
    force_scale: float = 0.03
    concept_perception_radius: float = 3.0
    initial_cell_count: int = 128
    max_population: int = 500
    spawn_spread: float = 0.3
    tradeoff_amplifier: float = 1.0
    concept_gravity_strength: float = 5.0
    concept_gravity_range: float = 5.0
    concept_gravity_sharpness: float = 50.0  # denominator scaling (matches JS engine)
    thermal_noise: float = 0.02
    boundary_mode: str = "soft"
    boundary_margin: float = 0.08
    boundary_strength: float = 0.05  # increased — primary boundary mechanism with soft clamp


@dataclass
class CellConfig:
    momentum: float = 0.95
    initial_energy: float = 75.0
    max_energy: float = 100.0
    reproduction_threshold: float = 0.75
    reproduction_transfer: float = 0.4
    child_spawn_offset: float = 0.1
    mutation_strength: float = 0.2


@dataclass
class MetabolismConfig:
    base_cost_per_dim: float = 0.005
    movement_cost_factor: float = 0.002
    concept_absorption_range: float = 3.0
    max_energy_gain_per_tick: float = 0.5
    energy_falloff_exponent: float = 2.0
    basin_penalty: float = 0.0
    min_energy: float = 0.1


@dataclass
class ConceptDynamicsConfig:
    min_separation: float = 0.3
    separation_strength: float = 2.0
    attract_scale: float = 0.5
    repel_scale: float = 0.3


@dataclass
class AfsSimulationConfig:
    engine: EngineConfig = field(default_factory=EngineConfig)
    cell: CellConfig = field(default_factory=CellConfig)
    metabolism: MetabolismConfig = field(default_factory=MetabolismConfig)
    concept_dynamics: ConceptDynamicsConfig = field(default_factory=ConceptDynamicsConfig)

    # Calibration metadata (set by Homeostasis)
    _calibration: Optional[dict] = field(default=None, repr=False)

    @classmethod
    def from_dict(cls, data: dict) -> "AfsSimulationConfig":
        """Create config from a dict, merging into defaults."""
        config = cls()
        if "engine" in data:
            for k, v in data["engine"].items():
                if hasattr(config.engine, k):
                    setattr(config.engine, k, v)
        if "cell" in data:
            for k, v in data["cell"].items():
                if hasattr(config.cell, k):
                    setattr(config.cell, k, v)
        if "metabolism" in data:
            for k, v in data["metabolism"].items():
                if hasattr(config.metabolism, k):
                    setattr(config.metabolism, k, v)
        if "concept_dynamics" in data:
            for k, v in data["concept_dynamics"].items():
                if hasattr(config.concept_dynamics, k):
                    setattr(config.concept_dynamics, k, v)
        return config

    def to_dict(self) -> dict:
        from dataclasses import asdict
        d = asdict(self)
        d.pop("_calibration", None)
        return d

    def to_json(self) -> str:
        return json.dumps(self.to_dict(), indent=2)
