"""
Unit tests for overlap detection.

Tests compute_id_overlap function for detecting shared identifiers
between train and test sets.
"""

import pytest
import pandas as pd
from mleakdetect.core.overlap import compute_id_overlap


def test_no_overlap():
    """
    Test when train and test have completely different IDs.

    This is the ideal case - zero overlap.
    """
    train = pd.DataFrame({
        'user_id': ['A', 'B', 'C'],
        'value': [1, 2, 3]
    })

    test = pd.DataFrame({
        'user_id': ['D', 'E', 'F'],
        'value': [4, 5, 6]
    })

    result = compute_id_overlap(train, test, id_columns=['user_id'])

    # Should have zero overlap
    assert result.loc[0, 'overlap_count'] == 0
    assert result.loc[0, 'overlap_percentage_test'] == 0.0

    print(f"No overlap detected")
    print(f"Train IDs: {result.loc[0, 'unique_train']}")
    print(f"Test IDs: {result.loc[0, 'unique_test']}")
    print(f"Overlap: {result.loc[0, 'overlap_count']}")


def test_complete_overlap():
    """
    Test when all test IDs appear in training set.

    This is a problem - 100% overlap.
    """
    train = pd.DataFrame({
        'user_id': ['A', 'B', 'C', 'D', 'E'],
        'value': [1, 2, 3, 4, 5]
    })

    test = pd.DataFrame({
        'user_id': ['A', 'B', 'C'],
        'value': [10, 20, 30]
    })

    result = compute_id_overlap(train, test, id_columns=['user_id'])

    # All test IDs appear in train
    assert result.loc[0, 'overlap_count'] == 3
    assert result.loc[0, 'overlap_percentage_test'] == 100.0


def test_partial_overlap():
    """
    Test when some test IDs appear in training set.

    This is a common scenario.
    """
    train = pd.DataFrame({
        'device_id': ['A', 'B', 'C', 'D'],
        'value': [1, 2, 3, 4]
    })

    test = pd.DataFrame({
        'device_id': ['C', 'D', 'E', 'F'],
        'value': [5, 6, 7, 8]
    })

    result = compute_id_overlap(train, test, id_columns=['device_id'])

    # C and D overlap (2 out of 4 test IDs)
    assert result.loc[0, 'overlap_count'] == 2
    assert result.loc[0, 'overlap_percentage_test'] == 50.0


def test_multiple_columns():
    """
    Test overlap analysis for multiple ID columns.

    Should analyze each column separately.
    """
    train = pd.DataFrame({
        'user_id': ['A', 'B', 'C'],
        'device_id': ['D1', 'D2', 'D3'],
        'value': [1, 2, 3]
    })

    test = pd.DataFrame({
        'user_id': ['A', 'B', 'X'],  # 2/3 overlap
        'device_id': ['D1', 'D4', 'D5'],  # 1/3 overlap
        'value': [4, 5, 6]
    })

    result = compute_id_overlap(
        train, test,
        id_columns=['user_id', 'device_id']
    )

    # Should have 2 rows (one per column)
    assert len(result) == 2

    # user_id: 2/3 = 66.67%
    user_row = result[result['column'] == 'user_id'].iloc[0]
    assert user_row['overlap_count'] == 2
    assert abs(user_row['overlap_percentage_test'] - 66.67) < 0.1

    # device_id: 1/3 = 33.33%
    device_row = result[result['column'] == 'device_id'].iloc[0]
    assert device_row['overlap_count'] == 1
    assert abs(device_row['overlap_percentage_test'] - 33.33) < 0.1

    print(f"Multiple columns analyzed")
    print(f"user_id overlap: {user_row['overlap_percentage_test']:.2f}%")
    print(f"device_id overlap: {device_row['overlap_percentage_test']:.2f}%")


def test_nan_handling():
    """
    Test that NaN values are handled correctly.

    With dropna=True, NaN should be ignored.
    """
    train = pd.DataFrame({
        'id': ['A', 'B', None, 'C'],
        'value': [1, 2, 3, 4]
    })

    test = pd.DataFrame({
        'id': ['A', None, 'D'],
        'value': [5, 6, 7]
    })

    result = compute_id_overlap(train, test, id_columns=['id'], dropna=True)

    # Train: A, B, C (ignoring None)
    # Test: A, D (ignoring None)
    # Overlap: A (1 out of 2 test IDs)
    assert result.loc[0, 'unique_train'] == 3
    assert result.loc[0, 'unique_test'] == 2
    assert result.loc[0, 'overlap_count'] == 1
    assert result.loc[0, 'overlap_percentage_test'] == 50.0


def test_duplicate_ids_in_splits():
    """
    Test when same ID appears multiple times in one split.

    Should count unique IDs only.
    """
    train = pd.DataFrame({
        'user_id': ['A', 'A', 'A', 'B', 'B'],
        'value': [1, 2, 3, 4, 5]
    })

    test = pd.DataFrame({
        'user_id': ['A', 'C', 'C'],
        'value': [6, 7, 8]
    })

    result = compute_id_overlap(train, test, id_columns=['user_id'])

    # Train has unique IDs: A, B (2 unique)
    # Test has unique IDs: A, C (2 unique)
    # Overlap: A (1)
    assert result.loc[0, 'unique_train'] == 2
    assert result.loc[0, 'unique_test'] == 2
    assert result.loc[0, 'overlap_count'] == 1
    assert result.loc[0, 'overlap_percentage_test'] == 50.0


def test_empty_test_set():
    """
    Test when test set is empty.

    Should handle gracefully with 0% overlap.
    """
    train = pd.DataFrame({
        'id': ['A', 'B', 'C'],
        'value': [1, 2, 3]
    })

    test = pd.DataFrame({
        'id': [],
        'value': []
    })

    result = compute_id_overlap(train, test, id_columns=['id'])

    # Empty test → 0 overlap
    assert result.loc[0, 'unique_test'] == 0
    assert result.loc[0, 'overlap_count'] == 0
    assert result.loc[0, 'overlap_percentage_test'] == 0.0


def test_sorted_by_overlap_percentage():
    """
    Test that results are sorted by overlap percentage.

    Highest overlap should come first.
    """
    train = pd.DataFrame({
        'col1': ['A', 'B', 'C'],
        'col2': ['X', 'Y', 'Z'],
        'col3': ['M', 'N', 'O']
    })

    test = pd.DataFrame({
        'col1': ['A', 'D', 'E'],  # 1/3 = 33.33%
        'col2': ['X', 'Y', 'W'],  # 2/3 = 66.67%
        'col3': ['P', 'Q', 'R']  # 0/3 = 0%
    })

    result = compute_id_overlap(
        train, test,
        id_columns=['col1', 'col2', 'col3']
    )

    # Should be sorted by overlap_percentage_test (descending)
    # col2 (66.67%) > col1 (33.33%) > col3 (0%)
    assert result.iloc[0]['column'] == 'col2'
    assert result.iloc[1]['column'] == 'col1'
    assert result.iloc[2]['column'] == 'col3'

    print(f"Results sorted by overlap percentage")
    for _, row in result.iterrows():
        print(f"  {row['column']}: {row['overlap_percentage_test']:.2f}%")


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

    test_no_overlap()
    test_complete_overlap()
    test_partial_overlap()
    test_multiple_columns()
    test_nan_handling()
    test_duplicate_ids_in_splits()
    test_empty_test_set()
    test_sorted_by_overlap_percentage()
