"""
Augmentation configuration module.

Provides dataclasses for configuring document image augmentations.
All default values are document-safe (conservative) to preserve text readability.
"""

from dataclasses import dataclass, field
from typing import Any


@dataclass
class AugmentationParams:
    """
    Parameters for a single augmentation type.

    Attributes:
        enabled: Whether this augmentation is enabled.
        probability: Probability of applying this augmentation (0.0 to 1.0).
        params: Type-specific parameters dictionary.
    """

    enabled: bool = False
    probability: float = 0.5
    params: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "enabled": self.enabled,
            "probability": self.probability,
            "params": dict(self.params),
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "AugmentationParams":
        """Create from dictionary."""
        return cls(
            enabled=data.get("enabled", False),
            probability=data.get("probability", 0.5),
            params=dict(data.get("params", {})),
        )


def _default_perspective_warp() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.3,
        params={"max_warp": 0.02},  # Very conservative - 2% max distortion
    )


def _default_wrinkle() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.3,
        params={"intensity": 0.3, "num_wrinkles": (2, 5)},
    )


def _default_edge_damage() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={"max_damage_ratio": 0.05},  # Max 5% of edge damaged
    )


def _default_stain() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={
            "num_stains": (1, 3),
            "max_radius_ratio": 0.1,
            "opacity": (0.1, 0.3),
        },
    )


def _default_lighting_variation() -> AugmentationParams:
    return AugmentationParams(
        enabled=True,  # Safe default, commonly needed
        probability=0.5,
        params={
            "brightness_range": (-0.1, 0.1),
            "contrast_range": (0.9, 1.1),
        },
    )


def _default_shadow() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.3,
        params={"num_shadows": (1, 2), "opacity": (0.2, 0.4)},
    )


def _default_gaussian_blur() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={"kernel_size": (3, 5), "sigma": (0.5, 1.5)},
    )


def _default_motion_blur() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={"kernel_size": (5, 9), "angle_range": (-45, 45)},
    )


def _default_gaussian_noise() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.3,
        params={"mean": 0, "std": (5, 15)},  # Conservative noise levels
    )


def _default_salt_pepper() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={"amount": (0.001, 0.005)},  # Very sparse
    )


def _default_paper_texture() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.3,
        params={"texture_type": "random", "intensity": (0.05, 0.15)},
    )


def _default_scanner_artifacts() -> AugmentationParams:
    return AugmentationParams(
        enabled=False,
        probability=0.2,
        params={"line_probability": 0.3, "dust_probability": 0.4},
    )


@dataclass
class AugmentationConfig:
    """
    Complete augmentation configuration.

    All augmentation types have document-safe defaults that preserve
    text readability. Only lighting_variation is enabled by default.

    Attributes:
        perspective_warp: Geometric perspective transform (affects bboxes).
        wrinkle: Paper wrinkle/crease simulation.
        edge_damage: Damaged/torn edge effects.
        stain: Coffee stain/smudge effects.
        lighting_variation: Brightness and contrast variation.
        shadow: Shadow overlay effects.
        gaussian_blur: Gaussian blur for focus issues.
        motion_blur: Motion blur simulation.
        gaussian_noise: Gaussian noise for sensor noise.
        salt_pepper: Salt and pepper noise.
        paper_texture: Paper texture overlay.
        scanner_artifacts: Scanner line and dust artifacts.
        preserve_bboxes: Whether to adjust bboxes for geometric transforms.
        seed: Random seed for reproducibility.
    """

    # Geometric transforms (affects bboxes)
    perspective_warp: AugmentationParams = field(
        default_factory=_default_perspective_warp
    )

    # Degradation effects
    wrinkle: AugmentationParams = field(default_factory=_default_wrinkle)
    edge_damage: AugmentationParams = field(default_factory=_default_edge_damage)
    stain: AugmentationParams = field(default_factory=_default_stain)

    # Lighting effects
    lighting_variation: AugmentationParams = field(
        default_factory=_default_lighting_variation
    )
    shadow: AugmentationParams = field(default_factory=_default_shadow)

    # Blur effects
    gaussian_blur: AugmentationParams = field(default_factory=_default_gaussian_blur)
    motion_blur: AugmentationParams = field(default_factory=_default_motion_blur)

    # Noise effects
    gaussian_noise: AugmentationParams = field(default_factory=_default_gaussian_noise)
    salt_pepper: AugmentationParams = field(default_factory=_default_salt_pepper)

    # Texture effects
    paper_texture: AugmentationParams = field(default_factory=_default_paper_texture)
    scanner_artifacts: AugmentationParams = field(
        default_factory=_default_scanner_artifacts
    )

    # Global settings
    preserve_bboxes: bool = True
    seed: int | None = None

    # List of all augmentation field names
    _AUGMENTATION_FIELDS: tuple[str, ...] = (
        "perspective_warp",
        "wrinkle",
        "edge_damage",
        "stain",
        "lighting_variation",
        "shadow",
        "gaussian_blur",
        "motion_blur",
        "gaussian_noise",
        "salt_pepper",
        "paper_texture",
        "scanner_artifacts",
    )

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for serialization."""
        result: dict[str, Any] = {
            "preserve_bboxes": self.preserve_bboxes,
            "seed": self.seed,
        }

        for field_name in self._AUGMENTATION_FIELDS:
            params: AugmentationParams = getattr(self, field_name)
            result[field_name] = params.to_dict()

        return result

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "AugmentationConfig":
        """Create from dictionary."""
        kwargs: dict[str, Any] = {
            "preserve_bboxes": data.get("preserve_bboxes", True),
            "seed": data.get("seed"),
        }

        for field_name in cls._AUGMENTATION_FIELDS:
            if field_name in data:
                field_data = data[field_name]
                if isinstance(field_data, dict):
                    kwargs[field_name] = AugmentationParams.from_dict(field_data)

        return cls(**kwargs)

    def get_enabled_augmentations(self) -> list[str]:
        """Get list of enabled augmentation names."""
        enabled = []
        for field_name in self._AUGMENTATION_FIELDS:
            params: AugmentationParams = getattr(self, field_name)
            if params.enabled:
                enabled.append(field_name)
        return enabled

    def validate(self) -> None:
        """
        Validate configuration.

        Raises:
            ValueError: If any configuration value is invalid.
        """
        for field_name in self._AUGMENTATION_FIELDS:
            params: AugmentationParams = getattr(self, field_name)
            if not (0.0 <= params.probability <= 1.0):
                raise ValueError(
                    f"{field_name}.probability must be between 0 and 1, "
                    f"got {params.probability}"
                )
