"""Deterministic random service for cross-month consistency."""

from typing import List, Tuple
import polars as pl


class DeterministicRandomService:
    """Provides consistent cross-month resource allocation using hash-based selection."""

    def __init__(self, dataset_name: str):
        """Initialize service with dataset context for consistent seeding."""
        self.dataset_name = dataset_name

    def create_fingerprint(self, provider: str, service: str, resource_id: str, context: str) -> str:
        """
        Create deterministic fingerprint for consistent allocation across months.

        Args:
            provider: Cloud provider name
            service: Service name
            resource_id: Resource identifier
            context: Context for this allocation (e.g., key name, allocation type)

        Returns:
            Deterministic fingerprint string
        """
        return f"{self.dataset_name}:{provider}:{service}:{resource_id}:{context}"

    def select_value_for_resource(self, resource_fingerprint: str, values: List[str]) -> str:
        """
        Select value deterministically based on resource fingerprint.

        Args:
            resource_fingerprint: Unique fingerprint for resource + context
            values: List of possible values to choose from

        Returns:
            Deterministically selected value
        """
        if not values:
            raise ValueError("Values list cannot be empty")

        if len(values) == 1:
            return values[0]

        # Use built-in hash for deterministic selection
        hash_val = hash(resource_fingerprint) % len(values)
        return values[hash_val]

    def split_resources_50_50(self, df: pl.DataFrame, context: str) -> Tuple[pl.DataFrame, pl.DataFrame]:
        """
        Split DataFrame resources deterministically 50/50 based on fingerprint hashing.

        Args:
            df: DataFrame with resources (must have cloud_provider, service, resource_id)
            context: Context for this split (e.g., "non_k8s_split", "k8s_namespace_split")

        Returns:
            Tuple of (group_a, group_b) DataFrames split roughly 50/50
        """
        if df.height == 0:
            return df.clone(), df.clone()

        # Validate required columns
        required_cols = ['cloud_provider', 'service', 'resource_id']
        missing_cols = [col for col in required_cols if col not in df.columns]
        if missing_cols:
            raise ValueError(f"DataFrame missing required columns: {missing_cols}")

        # Create fingerprint for each resource
        fingerprint_col = pl.concat_str([
            pl.lit(self.dataset_name),
            pl.lit(":"),
            pl.col("cloud_provider"),
            pl.lit(":"),
            pl.col("service"),
            pl.lit(":"),
            pl.col("resource_id"),
            pl.lit(":"),
            pl.lit(context)
        ])

        # Use hash to determine group assignment (even/odd split)
        df_with_hash = df.with_columns([
            (fingerprint_col.hash() % 2).alias("_group_assignment")
        ])

        # Split based on hash result
        group_a = df_with_hash.filter(pl.col("_group_assignment") == 0).drop("_group_assignment")
        group_b = df_with_hash.filter(pl.col("_group_assignment") == 1).drop("_group_assignment")

        return group_a, group_b

    def allocate_values_to_resources(
        self,
        df: pl.DataFrame,
        values: List[str],
        context: str,
        column_name: str
    ) -> pl.DataFrame:
        """
        Allocate values to resources using deterministic selection.

        Args:
            df: DataFrame with resources
            values: List of values to allocate
            context: Context for fingerprint creation
            column_name: Name of column to create with allocated values

        Returns:
            DataFrame with new column containing allocated values
        """
        if df.height == 0:
            return df

        if not values:
            raise ValueError("Values list cannot be empty")

        # Validate required columns
        required_cols = ['cloud_provider', 'service', 'resource_id']
        missing_cols = [col for col in required_cols if col not in df.columns]
        if missing_cols:
            raise ValueError(f"DataFrame missing required columns: {missing_cols}")

        # Create fingerprint for each resource
        fingerprint_col = pl.concat_str([
            pl.lit(self.dataset_name),
            pl.lit(":"),
            pl.col("cloud_provider"),
            pl.lit(":"),
            pl.col("service"),
            pl.lit(":"),
            pl.col("resource_id"),
            pl.lit(":"),
            pl.lit(context)
        ])

        # Single value - no selection needed
        if len(values) == 1:
            return df.with_columns([
                pl.lit(values[0]).alias(column_name)
            ])

        # Multiple values - use hash-based selection
        result = df.with_columns([
            (fingerprint_col.hash() % len(values)).alias("_value_index")
        ])

        # Create mapping from index to value using when/then chains
        value_mapping = pl.col("_value_index")
        for i, value in enumerate(values):
            if i == 0:
                value_mapping = pl.when(pl.col("_value_index") == i).then(pl.lit(value))
            else:
                value_mapping = value_mapping.when(pl.col("_value_index") == i).then(pl.lit(value))

        # Apply mapping and clean up
        result = result.with_columns([
            value_mapping.otherwise(pl.lit(values[0])).alias(column_name)
        ]).drop("_value_index")

        return result

    def build_value_allocation_expression(
        self,
        values: List[str],
        context: str
    ) -> pl.Expr:
        """
        Build Polars expression for value allocation without creating intermediate DataFrames.

        Args:
            values: List of values to allocate
            context: Context for fingerprint creation

        Returns:
            Polars expression that can be used in with_columns()
        """
        if not values:
            raise ValueError("Values list cannot be empty")

        # Create fingerprint expression
        fingerprint_expr = pl.concat_str([
            pl.lit(self.dataset_name),
            pl.lit(":"),
            pl.col("cloud_provider"),
            pl.lit(":"),
            pl.col("service"),
            pl.lit(":"),
            pl.col("resource_id"),
            pl.lit(":"),
            pl.lit(context)
        ])

        # Single value - no selection needed
        if len(values) == 1:
            return pl.lit(values[0])

        # Multiple values - use hash-based selection
        value_index = fingerprint_expr.hash() % len(values)

        # Create value mapping expression using when/then chains
        value_expr = pl.when(value_index == 0).then(pl.lit(values[0]))
        for i, value in enumerate(values[1:], 1):
            value_expr = value_expr.when(value_index == i).then(pl.lit(value))

        return value_expr.otherwise(pl.lit(values[0]))