"""
Row Healer - Row-level structural healing.

Handles:
- Row length mismatches
- Partially corrupt rows
- Truncated/oversized rows
"""

from typing import List, Tuple, Optional
from dataclasses import dataclass
import pandas as pd
import numpy as np


@dataclass
class RowIssue:
    """Row-level structural issue."""
    row_index: int
    issue_type: str  # 'truncated', 'oversized', 'corrupt'
    expected_columns: int
    actual_columns: int
    severity: str  # 'low', 'medium', 'high'


@dataclass
class CorruptBlock:
    """Block of corrupt rows."""
    start_row: int
    end_row: int
    rows: pd.DataFrame
    issue_description: str


class RowHealer:
    """Heals row-level structural issues."""
    
    def detect_row_issues(
        self,
        df: pd.DataFrame,
        expected_columns: Optional[int] = None
    ) -> List[RowIssue]:
        """
        Detect row-level issues.
        
        Args:
            df: DataFrame to analyze
            expected_columns: Expected number of columns (inferred if None)
        """
        issues = []
        
        if expected_columns is None:
            # Infer from mode
            expected_columns = len(df.columns)
        
        # Check for rows with unexpected number of values
        # This requires parsing the raw CSV again
        # For now, we'll detect based on excessive NaNs
        
        for idx, row in df.iterrows():
            nan_count = row.isna().sum()
            total_cols = len(row)
            
            # If >50% NaN, likely truncated
            if nan_count / total_cols > 0.5:
                issues.append(RowIssue(
                    row_index=idx,
                    issue_type='truncated',
                    expected_columns=expected_columns,
                    actual_columns=total_cols - nan_count,
                    severity='medium'
                ))
        
        return issues
    
    def fix_truncated_rows(
        self,
        df: pd.DataFrame,
        fill_value=pd.NA
    ) -> pd.DataFrame:
        """
        Fix truncated rows by padding with fill_value.
        
        Args:
            df: DataFrame to fix
            fill_value: Value to use for padding
        """
        # Already handled by pandas - it fills with NaN
        return df.fillna(fill_value)
    
    def fix_oversized_rows(
        self,
        df: pd.DataFrame,
        expected_columns: int
    ) -> pd.DataFrame:
        """
        Fix oversized rows by truncating to expected columns.
        
        Args:
            df: DataFrame to fix
            expected_columns: Number of columns to keep
        """
        if len(df.columns) > expected_columns:
            return df.iloc[:, :expected_columns]
        return df
    
    def isolate_corrupt_blocks(
        self,
        df: pd.DataFrame,
        corruption_threshold: float = 0.7
    ) -> Tuple[pd.DataFrame, List[CorruptBlock]]:
        """
        Identify and isolate blocks of corrupt rows.
        
        Args:
            df: DataFrame to analyze
            corruption_threshold: Fraction of NaNs to consider corrupt
            
        Returns:
            Tuple of (clean_df, corrupt_blocks)
        """
        corrupt_blocks = []
        clean_rows = []
        
        current_block_start = None
        current_block_rows = []
        
        for idx, row in df.iterrows():
            nan_fraction = row.isna().sum() / len(row)
            
            if nan_fraction >= corruption_threshold:
                # Corrupt row
                if current_block_start is None:
                    current_block_start = idx
                current_block_rows.append(row)
            else:
                # Clean row
                if current_block_start is not None:
                    # End of corrupt block
                    corrupt_blocks.append(CorruptBlock(
                        start_row=current_block_start,
                        end_row=idx - 1,
                        rows=pd.DataFrame(current_block_rows),
                        issue_description=f"{len(current_block_rows)} corrupt rows"
                    ))
                    current_block_start = None
                    current_block_rows = []
                
                clean_rows.append(row)
        
        # Handle trailing corrupt block
        if current_block_start is not None:
            corrupt_blocks.append(CorruptBlock(
                start_row=current_block_start,
                end_row=len(df) - 1,
                rows=pd.DataFrame(current_block_rows),
                issue_description=f"{len(current_block_rows)} corrupt rows at end"
            ))
        
        clean_df = pd.DataFrame(clean_rows) if clean_rows else pd.DataFrame()
        
        return clean_df, corrupt_blocks
