"""
Centralized random operations service for metadata package.

Provides consistent, deterministic random operations with proper seeding.
"""

import logging
from typing import List, Optional, Tuple

import numpy as np


logger = logging.getLogger(__name__)


class DeterministicRandomService:
    """
    Centralized service for deterministic random operations.
    
    Ensures consistent seeding and provides vectorized random operations
    to replace scattered random.seed() and np.random calls.
    """
    
    def __init__(self, dataset_name: str):
        """
        Initialize random service with base dataset seed.
        
        Args:
            dataset_name: Base dataset name for seed generation
        """
        self.dataset_name = dataset_name
        self.base_seed = self._generate_base_seed(dataset_name)
        logger.info(f"Initialized random service with base seed: {self.base_seed}")
    
    def get_seeded_generator(self, stage: str, provider: Optional[str] = None) -> np.random.Generator:
        """
        Get a seeded random generator for a specific context.
        
        Args:
            stage: Processing stage (e.g., "metadata", "azure_rg", "tags", "regions")
            provider: Optional provider context (AWS, Azure, GCP)
            
        Returns:
            Numpy random generator with deterministic seed
        """
        context_parts = [self.dataset_name, stage]
        if provider:
            context_parts.append(provider)
            
        context = "_".join(context_parts)
        seed = abs(hash(context)) % (2**32)
        
        logger.debug(f"Generated seed {seed} for context: {context}")
        return np.random.Generator(np.random.PCG64(seed))
    
    def vectorized_choice(
        self, 
        choices: List[str], 
        size: int, 
        weights: Optional[List[float]] = None,
        stage: str = "default",
        provider: Optional[str] = None
    ) -> np.ndarray:
        """
        Vectorized random choice operation.
        
        Args:
            choices: List of choices to select from
            size: Number of selections to make
            weights: Optional weights for choices (must sum to 1.0)
            stage: Processing stage for seeding
            provider: Optional provider context
            
        Returns:
            Array of selected choices
        """
        rng = self.get_seeded_generator(stage, provider)
        return rng.choice(choices, size=size, p=weights)
    
    def vectorized_boolean_choice(
        self, 
        size: int, 
        true_probability: float = 0.5,
        stage: str = "default",
        provider: Optional[str] = None
    ) -> np.ndarray:
        """
        Vectorized boolean choice operation.
        
        Args:
            size: Number of boolean values to generate
            true_probability: Probability of True values (0.0 to 1.0)
            stage: Processing stage for seeding
            provider: Optional provider context
            
        Returns:
            Array of boolean values
        """
        rng = self.get_seeded_generator(stage, provider)
        return rng.choice([True, False], size=size, p=[true_probability, 1 - true_probability])
    
    def vectorized_uniform(
        self, 
        size: int,
        low: float = 0.0,
        high: float = 1.0,
        stage: str = "default",
        provider: Optional[str] = None
    ) -> np.ndarray:
        """
        Vectorized uniform random values.
        
        Args:
            size: Number of values to generate
            low: Lower bound (inclusive)
            high: Upper bound (exclusive)
            stage: Processing stage for seeding
            provider: Optional provider context
            
        Returns:
            Array of uniform random values
        """
        rng = self.get_seeded_generator(stage, provider)
        return rng.uniform(low, high, size)
    
    def single_random_value(
        self, 
        stage: str = "default",
        provider: Optional[str] = None
    ) -> float:
        """
        Generate single random value (0.0 to 1.0).
        
        Args:
            stage: Processing stage for seeding
            provider: Optional provider context
            
        Returns:
            Single random float value
        """
        rng = self.get_seeded_generator(stage, provider)
        return rng.random()
    
    def weighted_choice(self, choices: List[Tuple[str, float]], seed: str) -> str:
        """
        Deterministic weighted selection using individual seed string.
        
        Args:
            choices: List of (choice, weight) tuples
            seed: Individual seed string for this selection
            
        Returns:
            Selected choice
        """
        if not choices:
            return ""
        
        # Generate deterministic seed from input string
        seed_hash = abs(hash(seed)) % (2**32)
        rng = np.random.Generator(np.random.PCG64(seed_hash))
        
        # Extract choices and weights
        choice_values, weights = zip(*choices)
        
        # Normalize weights to ensure they sum to 1.0
        total_weight = sum(weights)
        if total_weight <= 0:
            # If all weights are 0 or negative, use uniform distribution
            normalized_weights = [1.0 / len(weights)] * len(weights)
        else:
            normalized_weights = [w / total_weight for w in weights]
        
        # Make deterministic weighted choice
        return rng.choice(choice_values, p=normalized_weights)
    
    def _generate_base_seed(self, dataset_name: str) -> int:
        """
        Generate base seed from dataset name.
        
        Args:
            dataset_name: Dataset name
            
        Returns:
            Deterministic seed value
        """
        return abs(hash(f"metadata_{dataset_name}")) % (2**31)