from typing import Iterator, Dict, Any, Type, Optional
from atomingest.normalization.core import NormalizationReport, NormalizationStepConfig


class BaseNormalizerStep:
    """
    Base class for normalization steps.
    Implements default no-op behavior for both line and row processing.
    """

    def __init__(
        self,
        config: Dict[str, Any],
        report: NormalizationReport,
        strict: bool = False,
    ):
        """
        Initialize a normalization step.

        Args:
            config: Configuration dictionary for the step
            report: NormalizationReport for tracking processing metrics
            strict: If True, enable strict mode (fail on warnings)
        """
        self.config = config
        self.report = report
        self.strict = strict

    def process_lines(self, lines: Iterator[str]) -> Iterator[str]:
        """
        File-level transformation (default: yield unchanged).
        Override for BOM removal, header skipping, trailer dropping, etc.
        """
        yield from lines

    def process_row(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """
        Row-level transformation (default: yield unchanged).
        Override for trimming whitespace, null token normalization, column name normalization, etc.
        """
        return row


_STEP_REGISTRY: Dict[str, Type[BaseNormalizerStep]] = {}


def register_step(step_name: str):
    """
    Decorator to register a normalization step class with a given name.
    """

    def decorator(step_cls: Type[BaseNormalizerStep]) -> Type[BaseNormalizerStep]:
        if step_name in _STEP_REGISTRY:
            raise ValueError(f"Step '{step_name}' is already registered.")
        _STEP_REGISTRY[step_name] = step_cls
        return step_cls

    return decorator


class StepFactory:
    """
    Factory to create normalization step instances based on configuration.
    """

    @staticmethod
    def create_step(
        step_config: NormalizationStepConfig,
        report: NormalizationReport,
        strict: bool = False,
    ) -> BaseNormalizerStep:
        step_name = step_config.step
        step_class = _STEP_REGISTRY.get(step_name)

        if not step_class:
            available_steps = ", ".join(sorted(_STEP_REGISTRY.keys()))
            raise ValueError(
                f"Normalization step '{step_name}' is not registered. "
                f"Available steps: {available_steps}"
            )

        return step_class(
            config=step_config.config,
            report=report,
            strict=strict,
        )
