"""
Data quality validation functions for generated billing data.
"""

from datetime import datetime
from typing import Optional
import polars as pl

from .models import ValidationResult
from ..profiles.mapper import (
    get_variance_profile_config, 
    get_anomaly_profile_config,
    list_available_variance_profiles,
    list_available_anomaly_profiles
)


def validate_profiles_exist(
    variance_profile: Optional[str] = None, 
    anomaly_profile: Optional[str] = None
) -> ValidationResult:
    """
    Validate that specified variance and anomaly profiles exist.
    
    Fail-fast validation to prevent expensive generation with invalid profiles.
    
    Args:
        variance_profile: Variance profile name to validate
        anomaly_profile: Anomaly profile name to validate (optional)
        
    Returns:
        ValidationResult indicating profile existence
    """
    if variance_profile:
        # This will raise ValueError if profile doesn't exist
        get_variance_profile_config(variance_profile)

    if anomaly_profile:
        # This will raise ValueError if profile doesn't exist
        get_anomaly_profile_config(anomaly_profile)

    return ValidationResult.success_result("All profiles exist and are valid")


def validate_reference_data_filter(filtered_df: pl.DataFrame) -> ValidationResult:
    """
    Validate that reference data filtering returned at least one record.

    If filters return 0 records, we should fail immediately rather than
    proceeding with generation that will produce no meaningful output.

    Args:
        filtered_df: DataFrame after applying reference data filters

    Returns:
        ValidationResult indicating success or failure
    """
    if filtered_df.height == 0:
        return ValidationResult.fail(
            "Reference data filters returned 0 records. "
            "Check your filter configuration against available reference data."
        )

    return ValidationResult.success_result(
        f"Reference data filter returned {filtered_df.height} records"
    )


def validate_basic_completeness(
    df: pl.DataFrame, 
    start_date: datetime, 
    end_date: datetime
) -> ValidationResult:
    """
    Validate basic completeness of generated data.

    "Basic completeness" definition:
    - Non-empty dataset (> 0 rows)
    - No null values in critical columns
    - All costs > 0 (no zero or negative costs)
    - Timestamp coverage spans the configured date range

    Args:
        df: Generated DataFrame to validate
        start_date: Expected start date
        end_date: Expected end date

    Returns:
        ValidationResult indicating completeness status
    """
    # Check non-empty dataset
    if df.height == 0:
        return ValidationResult.fail("Dataset is empty (0 rows)")

    # Check for null values in critical columns
    critical_columns = ["timestamp", "cost", "resource_id"]
    null_issues = []

    for col in critical_columns:
        if col in df.columns:
            null_count = df[col].null_count()
            if null_count > 0:
                null_issues.append(f"{col}: {null_count} null values")

    if null_issues:
        return ValidationResult.fail(
            f"Null values found in critical columns: {null_issues}"
        )

    # Check all costs > 0
    if "cost" in df.columns:
        min_cost = df["cost"].min()
        if min_cost <= 0:
            zero_cost_count = df.filter(pl.col("cost") <= 0).height
            return ValidationResult.fail(
                f"Found {zero_cost_count} records with cost <= 0. Minimum cost: {min_cost}"
            )

    # Check timestamp coverage
    if "timestamp" in df.columns:
        actual_min = df["timestamp"].min()
        actual_max = df["timestamp"].max()

        # Allow some tolerance for date boundaries
        if actual_min > start_date or actual_max < end_date:
            return ValidationResult.fail(
                f"Timestamp coverage insufficient. "
                f"Expected: {start_date} to {end_date}, "
                f"Actual: {actual_min} to {actual_max}"
            )

    return ValidationResult.success_result(
        f"Basic completeness passed: {df.height} rows, all costs > 0, date coverage OK"
    )


def validate_cost_variance(
    df: pl.DataFrame, 
    min_variance: float = 0.10
) -> ValidationResult:
    """
    Ensure costs show variance - flat costs are unacceptable for demo purposes.

    Validates that costs vary sufficiently to create compelling demo patterns.
    Uses both coefficient of variation and range-based variance checks.

    Args:
        df: Generated DataFrame with cost column
        min_variance: Minimum required variance ratio (default 10%)

    Returns:
        ValidationResult indicating variance adequacy
    """
    if df.height == 0:
        return ValidationResult.fail("Cannot validate variance on empty dataset")

    if "cost" not in df.columns:
        return ValidationResult.fail("Cost column missing for variance validation")

    costs = df["cost"]

    # Calculate basic statistics
    cost_std = costs.std()
    cost_mean = costs.mean()
    cost_min = costs.min()
    cost_max = costs.max()

    if cost_mean == 0:
        return ValidationResult.fail("Cost mean is zero - cannot calculate variance")

    # Calculate coefficient of variation (standard deviation / mean)
    coefficient_of_variation = cost_std / cost_mean if cost_mean > 0 else 0

    # Calculate range-based variance (max - min) / mean
    range_variance = (cost_max - cost_min) / cost_mean if cost_mean > 0 else 0

    # Pass if either variance measure meets threshold
    if coefficient_of_variation >= min_variance or range_variance >= min_variance:
        return ValidationResult.success_result(
            f"Cost variance adequate: CV={coefficient_of_variation:.3f}, "
            f"Range variance={range_variance:.3f} (threshold={min_variance})"
        )
    else:
        return ValidationResult.fail(
            f"Insufficient cost variance (flat costs detected). "
            f"CV={coefficient_of_variation:.3f}, Range variance={range_variance:.3f}, "
            f"Required threshold={min_variance}. Check variance profile configuration."
        )


def validate_cost_conservation(
    df: pl.DataFrame, 
    expected_cost: float, 
    tolerance: float = 0.05
) -> ValidationResult:
    """
    Validate that total generated cost is within tolerance of expected cost.

    Essential for maintaining financial realism in demo scenarios.

    Args:
        df: Generated DataFrame with cost column
        expected_cost: Expected total cost from configuration
        tolerance: Allowed variance as decimal (0.05 = 5%)

    Returns:
        ValidationResult indicating cost conservation status
    """
    if df.height == 0:
        return ValidationResult.fail(
            "Cannot validate cost conservation on empty dataset"
        )

    if "cost" not in df.columns:
        return ValidationResult.fail(
            "Cost column missing for cost conservation validation"
        )

    if expected_cost <= 0:
        return ValidationResult.fail(f"Invalid expected cost: {expected_cost}")

    # Calculate total generated cost
    total_generated_cost = df["cost"].sum()

    # Calculate variance percentage
    variance = abs(total_generated_cost - expected_cost)
    variance_pct = (variance / expected_cost) * 100

    if variance_pct <= (tolerance * 100):
        return ValidationResult.success_result(
            f"Cost conservation OK: Generated ${total_generated_cost:.2f}, "
            f"Expected ${expected_cost:.2f}, Variance {variance_pct:.2f}%"
        )
    else:
        return ValidationResult.fail(
            f"Cost conservation violated: Generated ${total_generated_cost:.2f}, "
            f"Expected ${expected_cost:.2f}, Variance {variance_pct:.2f}% "
            f"(tolerance {tolerance * 100}%)"
        )