"""
Unit tests for duplicate detection functions.

Tests exact and near-duplicate detection.
"""

import pytest
import pandas as pd
from mleakdetect.core.duplicates import (
    find_exact_duplicates,
    remove_exact_duplicates,
    find_near_duplicates,
)

def test_find_exact_duplicates_basic():
    """
    Test finding exact duplicate rows.

    Should identify rows that are completely identical.
    """
    # Create data with duplicates
    df = pd.DataFrame({
        'A': [1, 2, 1, 3],
        'B': [10, 20, 10, 30],
        'C': ['x', 'y', 'x', 'z']
    })

    duplicates, summary = find_exact_duplicates(df, keep='first')

    # Row 0 and row 2 are identical
    # With keep='first', row 2 should be marked as duplicate
    assert len(duplicates) == 1, f"Expected 1 duplicate, got {len(duplicates)}"
    assert 2 in duplicates.index, "Row 2 should be marked as duplicate"

    # Check summary
    assert summary.n_rows == 4
    assert summary.exact_duplicate_rows == 1

    print(f"Found exact duplicates: {summary}")


def test_remove_exact_duplicates():
    """
    Test removing duplicate rows.

    Should keep only unique rows.
    """
    df = pd.DataFrame({
        'A': [1, 2, 1, 3, 2],
        'B': [10, 20, 10, 30, 20]
    })

    clean = remove_exact_duplicates(df, keep='first')

    # Should have 3 unique rows: [1,10], [2,20], [3,30]
    assert len(clean) == 3, f"Expected 3 unique rows, got {len(clean)}"

    # Check that duplicates are removed
    assert clean['A'].tolist() == [1, 2, 3]
    assert clean['B'].tolist() == [10, 20, 30]

    print(f"Removed duplicates: {len(df)} → {len(clean)} rows")


def test_no_duplicates():
    """
    Test dataset with no duplicates.

    Should return empty result.
    """
    df = pd.DataFrame({
        'A': [1, 2, 3, 4],
        'B': [10, 20, 30, 40]
    })

    duplicates, summary = find_exact_duplicates(df)

    # No duplicates
    assert len(duplicates) == 0
    assert summary.exact_duplicate_rows == 0
    assert summary.exact_duplicate_percentage == 0.0

    print(f"No duplicates found: {summary}")


def test_find_near_duplicates_basic():
    """
    Test finding near-duplicate rows.

    Rows with high similarity should be detected.
    """
    # Create data with near-duplicates
    df = pd.DataFrame({
        'feature1': [1.0, 1.1, 5.0, 5.1],
        'feature2': [2.0, 2.1, 10.0, 10.2],
        'feature3': [3.0, 3.0, 15.0, 15.0]
    })

    # Rows 0 and 1 are very similar
    # Rows 2 and 3 are very similar
    pairs, summary, df_used = find_near_duplicates(df, threshold=0.95)

    # Should find at least some pairs
    assert len(pairs) > 0, "Should find near-duplicate pairs"

    print(f"Found near-duplicates: {summary}")
    print(f"Pairs: {pairs}")


def test_near_duplicates_high_threshold():
    """
    Test near-duplicates with very high threshold.

    With threshold=0.99, only almost-identical rows should match.
    """
    df = pd.DataFrame({
        'x': [1.0, 1.5, 10.0],
        'y': [2.0, 2.5, 20.0]
    })

    # High threshold - very strict
    pairs, summary, df_used = find_near_duplicates(df, threshold=0.99)

    # These rows are quite different, so no pairs expected
    assert len(pairs) == 0, "High threshold should find few/no pairs"

    print(f"High threshold (0.99): {len(pairs)} pairs")


def test_exact_duplicates_with_subset():
    """
    Test finding duplicates based on specific columns only.

    Should only consider specified columns.
    """
    df = pd.DataFrame({
        'id': [1, 2, 3],
        'value': [100, 100, 200],
        'other': ['a', 'b', 'c']
    })

    # Check duplicates in 'value' column only
    duplicates, summary = find_exact_duplicates(df, subset=['value'], keep='first')

    # Rows 0 and 1 have same 'value' (100)
    assert len(duplicates) == 1
    assert 1 in duplicates.index

    print(f"Subset duplicates: {summary}")


def test_remove_duplicates_keep_last():
    """
    Test remove duplicates keeping last occurrence.
    """
    df = pd.DataFrame({
        'A': [1, 1, 2],
        'B': [10, 10, 20]
    })

    # Keep last duplicate
    clean = remove_exact_duplicates(df, keep='last')

    # Should keep row 1 (last occurrence of [1,10])
    assert len(clean) == 2
    assert clean.index.tolist() == [0, 1]  # After reset_index, becomes [0, 1]

    print(f"Keep last: {len(clean)} rows retained")


def test_all_duplicates():
    """
    Test dataset where all rows are duplicates.
    """
    df = pd.DataFrame({
        'A': [5, 5, 5, 5],
        'B': [10, 10, 10, 10]
    })

    clean = remove_exact_duplicates(df, keep='first')

    # Should keep only 1 row
    assert len(clean) == 1

    # Check summary
    duplicates, summary = find_exact_duplicates(df, keep='first')
    assert summary.exact_duplicate_rows == 3  # 3 duplicates
    assert summary.exact_duplicate_percentage == 75.0  # 3/4 = 75%

    print(f"All duplicates: {summary}")


if __name__ == "__main__":
    # Run tests manually
    print("\n=== Running Duplicate Detection Tests ===\n")

    test_find_exact_duplicates_basic()
    test_remove_exact_duplicates()
    test_no_duplicates()
    test_find_near_duplicates_basic()
    test_near_duplicates_high_threshold()
    test_exact_duplicates_with_subset()
    test_remove_duplicates_keep_last()
    test_all_duplicates()