"""
Column normalization service following Single Responsibility Principle.
"""

from typing import Dict
import polars as pl


class ColumnNormalizer:
    """
    Single responsibility: Normalize export column names and clean data.

    Handles CloudZero export variations and data cleaning for consistent processing.
    """

    # Standard column mapping for CloudZero exports
    COLUMN_MAPPING = {
        "Cloud Provider": "cloud_provider",
        "Service": "service",
        "Usage Family": "usage_family",
        "Resource Type": "resource_type",
        "Operation": "operation",
        "Usage Type": "usage_type",
        "Instance Type": "instance_type",  # Now included in standard mapping
        "Pricing Unit": "pricing_unit",
        "Real Cost": "real_cost",
        "Usage Amount": "usage_amount",
        # Handle variations
        "CloudProvider": "cloud_provider",
        "UsageFamily": "usage_family",
        "ResourceType": "resource_type",
        "UsageType": "usage_type",
        "InstanceType": "instance_type",
        "PricingUnit": "pricing_unit"
    }

    def normalize_export_columns(self, export_df: pl.DataFrame) -> pl.DataFrame:
        """
        Normalize column names and clean export data.

        Args:
            export_df: Raw export DataFrame

        Returns:
            DataFrame with normalized columns and cleaned data
        """
        # Remove unnamed columns first
        cleaned_df = self._remove_unnamed_columns(export_df)

        # Apply column name mapping
        normalized_df = self._apply_column_mapping(cleaned_df)

        # Clean data types and values
        return self._clean_data_values(normalized_df)

    def _remove_unnamed_columns(self, df: pl.DataFrame) -> pl.DataFrame:
        """Remove unnamed index columns from exports."""
        unnamed_cols = [
            col for col in df.columns
            if col.strip() == "" or col.startswith("Unnamed")
        ]

        if unnamed_cols:
            df = df.drop(unnamed_cols)

        return df

    def _apply_column_mapping(self, df: pl.DataFrame) -> pl.DataFrame:
        """Apply standard column name mapping."""
        # Build rename mapping for existing columns only
        rename_map = {
            old_name: new_name
            for old_name, new_name in self.COLUMN_MAPPING.items()
            if old_name in df.columns
        }

        if rename_map:
            df = df.rename(rename_map)

        return df

    def _clean_data_values(self, df: pl.DataFrame) -> pl.DataFrame:
        """Clean and normalize data values."""
        # Apply basic string cleaning
        df = self._clean_string_columns(df)

        # Clean resource_type prefixes (CloudZero export format)
        df = self._clean_resource_type_prefixes(df)

        # Clean numeric columns
        df = self._clean_numeric_columns(df)

        return df

    def _clean_string_columns(self, df: pl.DataFrame) -> pl.DataFrame:
        """Apply basic string cleaning to text columns."""
        string_columns = [
            "cloud_provider", "service", "usage_family",
            "resource_type", "operation", "usage_type",
            "instance_type", "pricing_unit"
        ]

        existing_string_cols = [
            col for col in string_columns if col in df.columns
        ]

        if existing_string_cols:
            df = df.with_columns([
                pl.col(col).cast(pl.Utf8).fill_null("").str.strip_chars()
                for col in existing_string_cols
            ])

        return df

    def _clean_resource_type_prefixes(self, df: pl.DataFrame) -> pl.DataFrame:
        """Remove any service prefixes from resource_type values."""
        if "resource_type" not in df.columns:
            return df

        return df.with_columns([
            pl.col("resource_type")
            .str.replace(r"^[^:]+:\s*", "")  # Remove any "prefix: " pattern
            .str.strip_chars()  # Remove any remaining whitespace
            .alias("resource_type")
        ])

    def count_resource_types_needing_cleaning(self, df: pl.DataFrame) -> int:
        """Count how many resource_type values have prefixes that need cleaning."""
        if "resource_type" not in df.columns or df.is_empty():
            return 0

        return df.filter(
            pl.col("resource_type").is_not_null() &
            pl.col("resource_type").str.contains(r"^[^:]+:\s*")
        ).height

    def _clean_numeric_columns(self, df: pl.DataFrame) -> pl.DataFrame:
        """Clean and normalize numeric columns."""
        if "real_cost" in df.columns:
            df = df.with_columns([
                pl.col("real_cost")
                .str.replace_all(r"[\$,]", "")  # Remove $ and commas
                .cast(pl.Float64, strict=False)
                .fill_null(0.0)
            ])

        if "usage_amount" in df.columns:
            df = df.with_columns([
                pl.col("usage_amount")
                .str.replace_all(r"[\$,]", "")  # Remove $ and commas
                .cast(pl.Float64, strict=False)
                .fill_null(0.0)
            ])

        return df

    def get_normalized_schema_columns(self) -> Dict[str, pl.DataType]:
        """
        Get expected schema for normalized reference data.

        Returns:
            Dictionary mapping column names to data types
        """
        return {
            "cloud_provider": pl.Utf8,
            "service": pl.Utf8,
            "usage_family": pl.Utf8,
            "resource_type": pl.Utf8,
            "operation": pl.Utf8,
            "usage_type": pl.Utf8,
            "instance_type": pl.Utf8,  # Now included in standard schema
            "pricing_unit": pl.Utf8,
            "real_cost": pl.Float64,
            "usage_amount": pl.Float64,
            "cost_per_unit": pl.Float64,
            "has_pricing": pl.Boolean
        }