"""
Validation service for metadata stage.

Provides pre-processing and post-processing validation functionality.
"""

import json
import logging
from typing import Any, Dict, List

import polars as pl
from rich.console import Console


logger = logging.getLogger(__name__)


class ValidationService:
    """Service for validating metadata processing inputs and outputs."""
    
    def __init__(self):
        """Initialize validation service."""
        self.console = Console()
    
    def validate_input_schema(self, df: pl.DataFrame) -> List[str]:
        """
        Validate input DataFrame schema.
        
        Args:
            df: Input DataFrame to validate
            
        Returns:
            List of validation errors (empty if valid)
        """
        errors = []
        
        # Check for required columns
        required_columns = ['service', 'resource_id']
        missing_columns = [col for col in required_columns if col not in df.columns]
        
        if missing_columns:
            errors.append(f"Missing required columns: {missing_columns}")
        
        # Check for empty DataFrame
        if df.height == 0:
            errors.append("DataFrame is empty")
        
        # Check for null values in required columns
        if 'service' in df.columns:
            null_services = df.select(pl.col('service').is_null().sum()).item()
            if null_services > 0:
                errors.append(f"Found {null_services} null values in 'service' column")
        
        if 'resource_id' in df.columns:
            null_resource_ids = df.select(pl.col('resource_id').is_null().sum()).item()
            if null_resource_ids > 0:
                errors.append(f"Found {null_resource_ids} null values in 'resource_id' column")
        
        return errors
    
    def validate_post_processing(self, df: pl.DataFrame, provider: str = None) -> Dict[str, Any]:
        """
        Validate post-processing DataFrame.
        
        Args:
            df: Processed DataFrame to validate
            provider: Cloud provider name (for provider-specific validation)
            
        Returns:
            Dictionary with validation results
        """
        results = {
            'total_rows': df.height,
            'tag_columns_validated': 0,
            'k8s_labels_validated': 0,
            'errors': [],
            'warnings': []
        }
        
        # Check for new tag columns
        tag_columns = [col for col in df.columns if col.startswith('resource/tag:')]
        
        for tag_col in tag_columns:
            non_null_count = df.select(pl.col(tag_col).is_not_null().sum()).item()
            
            # Skip provider-specific validation for irrelevant providers
            if provider and self._should_skip_provider_tag_validation(tag_col, provider):
                logger.debug(f"Skipping validation for provider-specific tag '{tag_col}' (provider: {provider})")
                continue
            
            if non_null_count == 0:
                results['errors'].append(f"Tag column '{tag_col}' has no non-null values")
            else:
                results['tag_columns_validated'] += 1
                logger.debug(f"Tag column '{tag_col}' has {non_null_count} non-null values")
        
        # Check K8s labels if present
        if 'k8s_labels' in df.columns:
            k8s_labels_col = df.select(pl.col('k8s_labels')).to_series()
            non_empty_labels = 0
            invalid_json_count = 0
            duplicate_keys_count = 0
            
            # Check if dataset has any K8s resources by looking at k8s_cluster column
            has_k8s_resources = False
            if 'k8s_cluster' in df.columns:
                k8s_cluster_count = df.select(pl.col('k8s_cluster').is_not_null().sum()).item()
                has_k8s_resources = k8s_cluster_count > 0
            
            for i, label_str in enumerate(k8s_labels_col):
                if label_str is None or label_str == '':
                    continue
                    
                # Check if it's non-empty
                if label_str.strip() not in ['', '{}']:
                    non_empty_labels += 1
                    
                    # Validate JSON format
                    try:
                        label_dict = json.loads(label_str)
                        
                        # Check for duplicate keys (would be caught by json.loads but double-check)
                        if isinstance(label_dict, dict):
                            # If we can parse it as dict, no duplicate keys
                            pass
                        else:
                            results['errors'].append(f"K8s label at row {i} is not a valid JSON object")
                            
                    except json.JSONDecodeError:
                        invalid_json_count += 1
                        if invalid_json_count <= 5:  # Limit error reporting
                            results['errors'].append(f"Invalid JSON in K8s labels at row {i}: {label_str}")
            
            # Only require non-empty K8s labels if the dataset actually has K8s resources
            if non_empty_labels == 0:
                if has_k8s_resources:
                    results['errors'].append("K8s labels column has no non-empty JSON values but dataset contains K8s resources")
                else:
                    logger.info("K8s labels are empty as expected - dataset contains no K8s resources")
                    results['k8s_labels_validated'] = 0  # Valid case for non-K8s datasets
            else:
                results['k8s_labels_validated'] = non_empty_labels
                
            if invalid_json_count > 0:
                results['errors'].append(f"Found {invalid_json_count} invalid JSON values in K8s labels")
        
        # Check for basic data integrity
        if df.height == 0:
            results['errors'].append("Processed DataFrame is empty")
        
        # Check for required columns still present
        required_columns = ['service', 'resource_id']
        missing_columns = [col for col in required_columns if col not in df.columns]
        
        if missing_columns:
            results['errors'].append(f"Missing required columns after processing: {missing_columns}")
        
        return results
    
    def _should_skip_provider_tag_validation(self, tag_col: str, provider: str) -> bool:
        """
        Determine if a tag column validation should be skipped for a given provider.
        
        Args:
            tag_col: Tag column name
            provider: Cloud provider name
            
        Returns:
            True if validation should be skipped, False otherwise
        """
        # Map provider-specific tag prefixes
        provider_prefixes = {
            'AWS': ['resource/tag:aws:'],
            'Azure': ['resource/tag:azure:'],
            'GCP': ['resource/tag:gcp', 'resource/tag:gcp_cz:']
        }
        
        # Skip validation for other providers' tags
        current_provider_prefixes = provider_prefixes.get(provider, [])
        
        # If it's a provider-specific tag, check if it matches current provider
        for other_provider, prefixes in provider_prefixes.items():
            if other_provider != provider:
                for prefix in prefixes:
                    if tag_col.startswith(prefix):
                        # This is another provider's tag, skip validation
                        return True
        
        return False
    
    def generate_validation_report(self, validation_results: Dict[str, Any], provider: str, month: str) -> str:
        """
        Generate a validation report string.
        
        Args:
            validation_results: Results from validate_post_processing
            provider: Cloud provider name
            month: Month in YYYY-MM format
            
        Returns:
            Formatted validation report
        """
        report_lines = [
            f"Metadata Validation Report - {provider}/{month}",
            "=" * 50,
            f"Total rows processed: {validation_results['total_rows']:,}",
            f"Tag columns validated: {validation_results['tag_columns_validated']}",
            f"K8s labels validated: {validation_results['k8s_labels_validated']}",
            ""
        ]
        
        # Add errors if any
        if validation_results['errors']:
            report_lines.append("❌ ERRORS:")
            for error in validation_results['errors']:
                report_lines.append(f"  • {error}")
            report_lines.append("")
        
        # Add warnings if any
        if validation_results['warnings']:
            report_lines.append("⚠️  WARNINGS:")
            for warning in validation_results['warnings']:
                report_lines.append(f"  • {warning}")
            report_lines.append("")
        
        # Add summary
        if not validation_results['errors']:
            report_lines.append("✅ VALIDATION PASSED")
        else:
            report_lines.append("❌ VALIDATION FAILED")
        
        return "\n".join(report_lines)
    
    def save_validation_report(self, validation_results: Dict[str, Any], provider: str, month: str, output_path: str) -> None:
        """
        Save validation report to file.
        
        Args:
            validation_results: Results from validate_post_processing
            provider: Cloud provider name
            month: Month in YYYY-MM format
            output_path: Path to save report
        """
        report = self.generate_validation_report(validation_results, provider, month)
        
        try:
            with open(output_path, 'w') as f:
                f.write(report)
            
            logger.info(f"Saved validation report to: {output_path}")
            
        except Exception as e:
            logger.error(f"Failed to save validation report: {e}")
            raise
    
    def print_validation_summary(self, validation_results: Dict[str, Any], provider: str, month: str) -> None:
        """
        Print validation summary to console.
        
        Args:
            validation_results: Results from validate_post_processing
            provider: Cloud provider name
            month: Month in YYYY-MM format
        """
        # Status icon based on errors
        status_icon = "✅" if not validation_results['errors'] else "❌"
        
        self.console.print(f"{status_icon} {provider}/{month}: {validation_results['total_rows']:,} rows processed")
        
        if validation_results['tag_columns_validated'] > 0:
            self.console.print(f"  📊 {validation_results['tag_columns_validated']} tag columns validated")
        
        if validation_results['k8s_labels_validated'] > 0:
            self.console.print(f"  🏷️  {validation_results['k8s_labels_validated']} K8s labels validated")
        
        # Show errors if any
        if validation_results['errors']:
            self.console.print(f"  ❌ {len(validation_results['errors'])} error(s) found")
            for error in validation_results['errors'][:3]:  # Show first 3 errors
                self.console.print(f"    • {error}")
            if len(validation_results['errors']) > 3:
                self.console.print(f"    • ... and {len(validation_results['errors']) - 3} more")