"""
Audit metadata injection for ingestion pipeline.

This module provides automatic injection of audit metadata (source file, run ID, timestamp)
into ingested records for traceability and compliance.
"""

import logging
import uuid
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional

from atomingest.pipeline.hooks import PreSaveHook, HookContext, HookRegistry

logger = logging.getLogger(__name__)


def generate_run_id() -> str:
    """
    Generate a unique run ID for an ingestion run.

    Returns:
        A unique identifier (UUID4) as a string.
    """
    return str(uuid.uuid4())


class MetadataInjectionHook(PreSaveHook):
    """
    Pre-save hook that automatically injects audit metadata into each record.

    Adds the following metadata fields to each row:
    - source_file: The path/name of the source file being ingested
    - run_id: A unique identifier for the ingestion run
    - ingestion_timestamp: The timestamp when the record was ingested (optional)

    Configuration (via config dict):
        - source_field_name: Name of the field for source file (default: "source_file")
        - run_id_field_name: Name of the field for run ID (default: "run_id")
        - timestamp_field_name: Name of the field for timestamp (default: "ingestion_timestamp")
        - include_timestamp: Whether to include ingestion timestamp (default: True)
        - include_full_path: Whether to include full file path or just filename (default: False)

    Usage in YAML:
        hooks:
          pre_save:
            - module: MetadataInjectionHook
              name: inject_audit_metadata
              config:
                source_field_name: source_file
                run_id_field_name: run_id
                include_timestamp: true
                include_full_path: false

    The hook expects the following in context.metadata:
        - source: The source file path
        - run_id: The run ID for this ingestion

    If these are not present, the hook will log a warning and skip injection.
    """

    def __init__(self, name: str = None, config: Dict[str, Any] = None):
        super().__init__(name or "MetadataInjectionHook", config)

        # Read configuration with defaults
        self.source_field_name = self.config.get("source_field_name", "source_file")
        self.run_id_field_name = self.config.get("run_id_field_name", "run_id")
        self.timestamp_field_name = self.config.get(
            "timestamp_field_name", "ingestion_timestamp"
        )
        self.include_timestamp = self.config.get("include_timestamp", True)
        self.include_full_path = self.config.get("include_full_path", False)

        logger.info(
            f"MetadataInjectionHook initialized with config: "
            f"source_field={self.source_field_name}, "
            f"run_id_field={self.run_id_field_name}, "
            f"timestamp_field={self.timestamp_field_name}, "
            f"include_timestamp={self.include_timestamp}, "
            f"include_full_path={self.include_full_path}"
        )

    def execute(self, context: HookContext) -> HookContext:
        """
        Inject audit metadata into the row data.

        Args:
            context: The hook context containing row_data and metadata.

        Returns:
            The modified context with metadata injected into row_data.
        """
        if not context.row_data:
            logger.warning("No row_data in context, skipping metadata injection")
            return context

        # Get source file path from metadata
        source = context.metadata.get("source")
        if not source:
            logger.warning(
                "No 'source' in context.metadata, cannot inject source metadata"
            )
        else:
            # Determine what to store: full path or just filename
            if self.include_full_path:
                source_value = str(source)
            else:
                source_value = Path(source).name

            # Inject source file into row data
            context.row_data[self.source_field_name] = source_value
            logger.debug(f"Injected source: {source_value}")

        # Get run ID from metadata
        run_id = context.metadata.get("run_id")
        if not run_id:
            logger.warning("No 'run_id' in context.metadata, cannot inject run_id")
        else:
            # Inject run ID into row data
            context.row_data[self.run_id_field_name] = run_id
            logger.debug(f"Injected run_id: {run_id}")

        # Optionally inject timestamp
        if self.include_timestamp:
            timestamp = datetime.now()
            context.row_data[self.timestamp_field_name] = timestamp
            logger.debug(f"Injected timestamp: {timestamp}")

        return context


def create_metadata_injection_hook(config: Optional[Dict[str, Any]] = None) -> MetadataInjectionHook:
    """
    Factory function to create a MetadataInjectionHook with configuration.

    Args:
        config: Optional configuration dictionary for the hook.

    Returns:
        A configured MetadataInjectionHook instance.
    """
    return MetadataInjectionHook(config=config or {})


# Register MetadataInjectionHook as a built-in hook
HookRegistry.register_builtin_hook("MetadataInjectionHook", MetadataInjectionHook)
