"""
Resource Classification Service.

Implements clean k8s_cluster-based resource classification for metadata enrichment.
Replaces complex service/resource type mapping with simple column-based logic.
"""

import logging
from typing import Dict, Tuple

import polars as pl

from .random_service import DeterministicRandomService


logger = logging.getLogger(__name__)


class ResourceClassificationService:
    """Service for classifying resources based on k8s_cluster column presence."""
    
    def __init__(self, random_service: DeterministicRandomService):
        """
        Initialize resource classification service.
        
        Args:
            random_service: Deterministic random service for consistent splits
        """
        self.random_service = random_service
        
        logger.info("Initialized ResourceClassificationService with k8s_cluster-based classification")
    
    def classify_resources(self, df: pl.DataFrame) -> Tuple[pl.DataFrame, pl.DataFrame]:
        """
        Split resources based on k8s_cluster column presence.
        
        Args:
            df: Input DataFrame with resources to classify
            
        Returns:
            Tuple of (k8s_resources, non_k8s_resources)
        """
        if df.height == 0:
            logger.info("Empty DataFrame provided for resource classification")
            return df.clone(), df.clone()
        
        # Check if k8s_cluster column exists - if not, treat all as non-K8s
        if 'k8s_cluster' not in df.columns:
            logger.info(f"No k8s_cluster column found - treating all {df.height:,} resources as non-K8s")
            # Return empty K8s DataFrame and all resources as non-K8s
            empty_k8s = df.head(0)  # Empty DataFrame with same schema
            return empty_k8s, df.clone()
        
        # K8s classification: must have non-null AND non-empty k8s_cluster (excluding whitespace-only)
        k8s_mask = (
            (pl.col('k8s_cluster').is_not_null()) & 
            (pl.col('k8s_cluster') != "") &
            (pl.col('k8s_cluster').str.strip_chars() != "")
        )
        
        k8s_resources = df.filter(k8s_mask)
        non_k8s_resources = df.filter(~k8s_mask)
        
        logger.info(f"Classified {df.height:,} resources: {k8s_resources.height:,} K8s, {non_k8s_resources.height:,} non-K8s")
        
        return k8s_resources, non_k8s_resources
    
    def determine_k8s_enrichment_splits(
        self, 
        k8s_resources: pl.DataFrame
    ) -> Tuple[pl.DataFrame, pl.DataFrame]:
        """
        Split K8s resources into labels vs namespace enrichment based on namespace semantics.
        
        - Default namespaces (default, kube-system, etc.) → feature labels
        - Custom namespaces → namespace injection  
        - Idle namespace → labels (no feature enrichment)
        
        Args:
            k8s_resources: DataFrame with K8s resources
            
        Returns:
            Tuple of (label_resources, namespace_resources)
        """
        if k8s_resources.height == 0:
            logger.info("No K8s resources to split for enrichment")
            return k8s_resources.clone(), k8s_resources.clone()
        
        # Define default K8s namespaces that should get feature labels
        default_namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
        
        # Semantic-based enrichment assignment
        enrichment_df = k8s_resources.with_columns([
            pl.when(pl.col('k8s_namespace') == '__UNALLOCATED__')
            .then(pl.lit('labels'))  # __UNALLOCATED__ gets labels but no feature enrichment
            .when(pl.col('k8s_namespace').is_in(default_namespaces))
            .then(pl.lit('labels'))  # Default namespaces get feature labels
            .when(
                (pl.col('k8s_namespace') == '') |
                (pl.col('k8s_namespace').is_null())
            )
            .then(pl.lit('labels'))  # Empty/null namespaces default to labels
            .otherwise(pl.lit('namespaces'))  # Custom namespaces get namespace injection
            .alias('_enrichment_type')
        ])
        
        # Split based on enrichment type
        label_resources = enrichment_df.filter(pl.col('_enrichment_type') == 'labels').drop('_enrichment_type')
        namespace_resources = enrichment_df.filter(pl.col('_enrichment_type') == 'namespaces').drop('_enrichment_type')
        
        # Get namespace-level statistics
        label_namespaces = label_resources.select('k8s_namespace').unique().height
        namespace_injection_namespaces = namespace_resources.select('k8s_namespace').unique().height
        
        logger.info(f"K8s enrichment split by namespace semantics: {label_resources.height:,} resources in {label_namespaces} namespaces → labels, {namespace_resources.height:,} resources in {namespace_injection_namespaces} namespaces → namespace injection")
        
        return label_resources, namespace_resources
    
    def determine_non_k8s_enrichment_splits(
        self, 
        non_k8s_resources: pl.DataFrame
    ) -> Tuple[pl.DataFrame, pl.DataFrame]:
        """
        Split non-K8s resources into tags vs resource ID enrichment (50/50).
        
        Args:
            non_k8s_resources: DataFrame with non-K8s resources
            
        Returns:
            Tuple of (tag_resources, resource_id_resources)
        """
        if non_k8s_resources.height == 0:
            logger.info("No non-K8s resources to split for enrichment")
            return non_k8s_resources.clone(), non_k8s_resources.clone()
        
        # Add deterministic enrichment type flag (vectorized)
        enrichment_df = non_k8s_resources.with_columns([
            # Create resource fingerprint and hash for 50/50 split (vectorized)
            pl.concat_str([
                pl.col('cloud_provider'),
                pl.lit(':'),
                pl.col('service'),
                pl.lit(':'),
                pl.col('resource_id'),
                pl.lit(':non_k8s_enrichment')
            ])
            .hash()
            .alias('_hash_value')
        ]).with_columns([
            # Deterministic 50/50 split based on hash (vectorized)
            pl.when((pl.col('_hash_value') % 2) == 0)
            .then(pl.lit('tags'))
            .otherwise(pl.lit('resource_ids'))
            .alias('_enrichment_type')
        ]).drop('_hash_value')
        
        # Split based on enrichment type
        tag_resources = enrichment_df.filter(pl.col('_enrichment_type') == 'tags').drop('_enrichment_type')
        resource_id_resources = enrichment_df.filter(pl.col('_enrichment_type') == 'resource_ids').drop('_enrichment_type')
        
        logger.info(f"Non-K8s enrichment split: {tag_resources.height:,} tags, {resource_id_resources.height:,} resource IDs")
        
        return tag_resources, resource_id_resources
    
    # Note: _determine_k8s_enrichment_type and _determine_non_k8s_enrichment_type methods
    # have been replaced with vectorized operations in the calling methods above
    
    def get_classification_stats(self, df: pl.DataFrame) -> Dict[str, int]:
        """
        Get resource classification statistics.
        
        Args:
            df: DataFrame to analyze
            
        Returns:
            Dictionary with classification statistics
        """
        if df.height == 0:
            return {
                'total_resources': 0,
                'k8s_resources': 0,
                'non_k8s_resources': 0,
                'k8s_percentage': 0.0,
                'non_k8s_percentage': 0.0
            }
        
        # Check if k8s_cluster column exists before using it
        if 'k8s_cluster' in df.columns:
            k8s_count = df.select(pl.col('k8s_cluster').is_not_null().sum()).item()
        else:
            k8s_count = 0  # No k8s_cluster column means no K8s resources
        
        non_k8s_count = df.height - k8s_count
        
        return {
            'total_resources': df.height,
            'k8s_resources': k8s_count,
            'non_k8s_resources': non_k8s_count,
            'k8s_percentage': (k8s_count / df.height) * 100,
            'non_k8s_percentage': (non_k8s_count / df.height) * 100
        }
    
    def validate_classification(self, k8s_resources: pl.DataFrame, non_k8s_resources: pl.DataFrame) -> Dict[str, bool]:
        """
        Validate resource classification results.
        
        Args:
            k8s_resources: DataFrame with classified K8s resources
            non_k8s_resources: DataFrame with classified non-K8s resources
            
        Returns:
            Dictionary with validation results
        """
        validation_results = {
            'k8s_have_cluster_column': True,
            'non_k8s_missing_cluster_column': True,
            'no_overlap': True,
            'all_resources_classified': True
        }
        
        # Validate K8s resources have k8s_cluster values
        if k8s_resources.height > 0:
            if 'k8s_cluster' in k8s_resources.columns:
                null_cluster_count = k8s_resources.select(pl.col('k8s_cluster').is_null().sum()).item()
                validation_results['k8s_have_cluster_column'] = (null_cluster_count == 0)
            else:
                validation_results['k8s_have_cluster_column'] = False  # K8s resources must have k8s_cluster column
        
        # Validate non-K8s resources don't have valid k8s_cluster values
        if non_k8s_resources.height > 0:
            if 'k8s_cluster' in non_k8s_resources.columns:
                # Valid k8s_cluster: not null, not empty, not whitespace-only
                # Use fill_null to handle null values before string operations
                valid_cluster_mask = (
                    (pl.col('k8s_cluster').is_not_null()) & 
                    (pl.col('k8s_cluster') != "") &
                    (pl.col('k8s_cluster').fill_null("").str.strip_chars() != "")
                )
                valid_cluster_count = non_k8s_resources.select(valid_cluster_mask.sum()).item()
                validation_results['non_k8s_missing_cluster_column'] = (valid_cluster_count == 0)
            else:
                validation_results['non_k8s_missing_cluster_column'] = True  # No k8s_cluster column is correct for non-K8s
        
        # Check for overlapping resource IDs (should be impossible with proper split)
        if k8s_resources.height > 0 and non_k8s_resources.height > 0:
            k8s_ids = set(k8s_resources.select('resource_id').to_series().to_list())
            non_k8s_ids = set(non_k8s_resources.select('resource_id').to_series().to_list())
            validation_results['no_overlap'] = len(k8s_ids.intersection(non_k8s_ids)) == 0
        
        return validation_results