"""
Data Quality Checker
====================

This module assesses data quality for the Sentiment140 dataset.
"""

import logging
import re
from pathlib import Path
from typing import Any, Dict, List

import pandas as pd

logger = logging.getLogger(__name__)


class DataQualityChecker:
    """
    Checks data quality and identifies potential issues in the dataset.
    """

    def __init__(self, df: pd.DataFrame):
        """
        Initialize the data quality checker.

        Args:
            df: DataFrame to check
        """
        self.df = df
        self.issues: List[str] = []

    def check_missing_values(self) -> Dict[str, Any]:
        """Check for missing values in the dataset."""
        missing = self.df.isnull().sum()
        missing_pct = missing / len(self.df) * 100

        result = {
            "has_missing": missing.sum() > 0,
            "missing_by_column": missing[missing > 0].to_dict(),
            "missing_percentage": missing_pct[missing_pct > 0].to_dict(),
        }

        if result["has_missing"]:
            self.issues.append(
                f"Missing values found in: {list(result['missing_by_column'].keys())}"
            )

        return result

    def check_duplicates(self) -> Dict[str, Any]:
        """Check for duplicate records."""
        # Check for exact duplicates
        exact_dupes = self.df.duplicated().sum()

        # Check for duplicate texts
        text_dupes = (
            self.df["text"].duplicated().sum() if "text" in self.df.columns else 0
        )

        result = {
            "exact_duplicates": int(exact_dupes),
            "duplicate_texts": int(text_dupes),
            "duplicate_percentage": float(exact_dupes / len(self.df) * 100),
        }

        if exact_dupes > 0:
            self.issues.append(f"Found {exact_dupes} exact duplicate records")
        if text_dupes > 0:
            self.issues.append(f"Found {text_dupes} duplicate texts")

        return result

    def check_text_quality(self) -> Dict[str, Any]:
        """Check quality of text data."""
        if "text" not in self.df.columns:
            return {"error": "No 'text' column found"}

        # Empty or very short texts
        empty_texts = (self.df["text"].str.len() == 0).sum()
        very_short = (self.df["text"].str.len() < 5).sum()

        # Texts with only special characters or numbers
        special_pattern = re.compile(r"^[^a-zA-Z]+$")
        only_special = (
            self.df["text"].apply(lambda x: bool(special_pattern.match(str(x)))).sum()
        )

        result = {
            "empty_texts": int(empty_texts),
            "very_short_texts": int(very_short),
            "only_special_chars": int(only_special),
            "total_issues": int(empty_texts + very_short + only_special),
        }

        if result["total_issues"] > 0:
            self.issues.append(f"Found {result['total_issues']} low-quality texts")

        return result

    def check_label_consistency(self) -> Dict[str, Any]:
        """Check if labels are consistent and valid."""
        if "target" not in self.df.columns:
            return {"error": "No 'target' column found"}

        expected_labels = {0, 2, 4}
        actual_labels = set(self.df["target"].unique())

        invalid_labels = actual_labels - expected_labels

        result = {
            "expected_labels": list(expected_labels),
            "actual_labels": list(actual_labels),
            "has_invalid_labels": len(invalid_labels) > 0,
            "invalid_labels": list(invalid_labels),
        }

        if result["has_invalid_labels"]:
            self.issues.append(f"Found invalid labels: {invalid_labels}")

        return result

    def run_all_checks(self) -> Dict[str, Any]:
        """Run all quality checks and return results."""
        logger.info("Running data quality checks...")

        self.issues = []  # Reset issues

        results = {
            "dataset_size": len(self.df),
            "missing_values": self.check_missing_values(),
            "duplicates": self.check_duplicates(),
            "text_quality": self.check_text_quality(),
            "label_consistency": self.check_label_consistency(),
            "issues_found": self.issues,
            "quality_score": self._calculate_quality_score(),
        }

        logger.info(
            f"Quality checks completed. Quality score: {results['quality_score']:.2f}/100"
        )

        return results

    def _calculate_quality_score(self) -> float:
        """Calculate an overall quality score (0-100)."""
        score = 100.0

        # Deduct points for issues
        missing = self.check_missing_values()
        if missing["has_missing"]:
            score -= 10

        dupes = self.check_duplicates()
        if dupes["exact_duplicates"] > 0:
            score -= min(20, dupes["duplicate_percentage"])

        text_qual = self.check_text_quality()
        if "total_issues" in text_qual:
            issue_pct = (text_qual["total_issues"] / len(self.df)) * 100
            score -= min(20, issue_pct)

        label_cons = self.check_label_consistency()
        if label_cons.get("has_invalid_labels", False):
            score -= 15

        return max(0.0, score)

    def generate_report(self) -> str:
        """Generate a data quality report."""
        results = self.run_all_checks()

        report = f"""
# Data Quality Report

## Overall Quality Score: {results['quality_score']:.1f}/100

## Dataset Size
- Total Records: {results['dataset_size']:,}

## Missing Values
- Has Missing: {results['missing_values']['has_missing']}
"""

        if results["missing_values"]["has_missing"]:
            report += "- Missing by Column:\n"
            for col, count in results["missing_values"]["missing_by_column"].items():
                pct = results["missing_values"]["missing_percentage"][col]
                report += f"  - {col}: {count} ({pct:.2f}%)\n"

        report += f"""
## Duplicates
- Exact Duplicates: {results['duplicates']['exact_duplicates']:,}
- Duplicate Texts: {results['duplicates']['duplicate_texts']:,}
- Duplicate Percentage: {results['duplicates']['duplicate_percentage']:.2f}%

## Text Quality
- Empty Texts: {results['text_quality'].get('empty_texts', 0):,}
- Very Short Texts: {results['text_quality'].get('very_short_texts', 0):,}
- Only Special Characters: {results['text_quality'].get('only_special_chars', 0):,}

## Label Consistency
- Expected Labels: {results['label_consistency'].get('expected_labels', [])}
- Actual Labels: {results['label_consistency'].get('actual_labels', [])}
- Has Invalid Labels: {results['label_consistency'].get('has_invalid_labels', False)}

## Issues Found
"""

        if results["issues_found"]:
            for issue in results["issues_found"]:
                report += f"- {issue}\n"
        else:
            report += "- No major issues found\n"

        return report


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)

    # Example usage
    data_path = Path("data/sentiment140/train.csv")
    colnames = ["target", "ids", "date", "flag", "user", "text"]
    df = pd.read_csv(data_path, encoding="latin-1", names=colnames, nrows=10000)

    checker = DataQualityChecker(df)
    print(checker.generate_report())
