"""
Unit tests for validation utilities.

Tests various validation functions to ensure proper error handling.
"""

import pytest
import pandas as pd
from mleakdetect.utils.validation import (
    validate_dataframe,
    validate_columns_exist,
    validate_time_column,
    validate_time_no_nat,
    validate_ratio,
    validate_alpha,
    validate_timedelta_string,
    validate_non_empty_dataframe,
)


def test_validate_dataframe_valid():
    """
    Test that valid DataFrame passes validation.
    """
    df = pd.DataFrame({'A': [1, 2, 3]})

    # Should not raise error
    validate_dataframe(df, name="test_df")


def test_validate_dataframe_invalid():
    """
    Test that non-DataFrame raises TypeError.
    """
    # Not a DataFrame
    not_df = [1, 2, 3]

    with pytest.raises(TypeError):
        validate_dataframe(not_df, name="test_df")


def test_validate_columns_exist_valid():
    """
    Test that existing columns pass validation.
    """
    df = pd.DataFrame({
        'A': [1, 2],
        'B': [3, 4],
        'C': [5, 6]
    })

    # Should not raise error
    validate_columns_exist(df, ['A', 'B'], name="df")


def test_validate_columns_exist_missing():
    """
    Test that missing columns raise ValueError.
    """
    df = pd.DataFrame({
        'A': [1, 2],
        'B': [3, 4]
    })

    # Column 'C' doesn't exist
    with pytest.raises(ValueError):
        validate_columns_exist(df, ['A', 'C'], name="df")


def test_validate_time_column_valid():
    """
    Test that valid time column passes.
    """
    df = pd.DataFrame({
        'date': pd.date_range('2024-01-01', periods=5)
    })

    # Should not raise error
    validate_time_column(df, 'date', name="df")


def test_validate_time_column_invalid():
    """
    Test that non-parseable time column raises error.
    """
    df = pd.DataFrame({
        'date': ['not', 'a', 'date']
    })

    with pytest.raises(ValueError):
        validate_time_column(df, 'date', name="df")


def test_validate_time_no_nat_valid():
    """
    Test that time column without NaT passes.
    """
    df = pd.DataFrame({
        'date': pd.date_range('2024-01-01', periods=5)
    })

    # Should not raise error
    validate_time_no_nat(df, 'date', name="df")


def test_validate_time_no_nat_invalid():
    """
    Test that time column with NaT raises error.
    """
    df = pd.DataFrame({
        'date': pd.to_datetime(['2024-01-01', None, '2024-01-03'])
    })

    with pytest.raises(ValueError):
        validate_time_no_nat(df, 'date', name="df")


def test_validate_ratio_valid():
    """
    Test that valid ratios pass.
    """
    # Valid ratios between 0 and 1
    validate_ratio(0.5, name="ratio", inclusive=False)
    validate_ratio(0.7, name="ratio", inclusive=False)

    # With inclusive=True, 0 and 1 are allowed
    validate_ratio(0.0, name="ratio", inclusive=True)
    validate_ratio(1.0, name="ratio", inclusive=True)


def test_validate_ratio_invalid():
    """
    Test that invalid ratios raise error.
    """
    # Ratio > 1
    with pytest.raises(ValueError):
        validate_ratio(1.5, name="ratio", inclusive=False)

    # Ratio < 0
    with pytest.raises(ValueError):
        validate_ratio(-0.5, name="ratio", inclusive=False)

    # Ratio = 0 with inclusive=False
    with pytest.raises(ValueError):
        validate_ratio(0.0, name="ratio", inclusive=False)

    # Ratio = 1 with inclusive=False
    with pytest.raises(ValueError):
        validate_ratio(1.0, name="ratio", inclusive=False)


def test_validate_alpha_valid():
    """
    Test that valid alpha values pass.
    """
    validate_alpha(0.0)
    validate_alpha(1.0)
    validate_alpha(5.0)


def test_validate_alpha_invalid():
    """
    Test that negative alpha raises error.
    """
    with pytest.raises(ValueError):
        validate_alpha(-1.0)

    # Non-numeric alpha
    with pytest.raises(TypeError):
        validate_alpha("1.0")


def test_validate_timedelta_string_valid():
    """
    Test that valid timedelta strings pass.
    """
    # Valid formats
    td1 = validate_timedelta_string("1D")
    assert td1 == pd.Timedelta(days=1)

    td2 = validate_timedelta_string("7D")
    assert td2 == pd.Timedelta(days=7)

    td3 = validate_timedelta_string("12h")
    assert td3 == pd.Timedelta(hours=12)

    td4 = validate_timedelta_string("0D")
    assert td4 == pd.Timedelta(days=0)


def test_validate_timedelta_string_invalid():
    """
    Test that invalid timedelta strings raise error.
    """
    # Invalid format
    with pytest.raises(ValueError):
        validate_timedelta_string("invalid")

    # Negative timedelta
    with pytest.raises(ValueError):
        validate_timedelta_string("-1D")

    # Not a string
    with pytest.raises(TypeError):
        validate_timedelta_string(123)


def test_validate_non_empty_dataframe_valid():
    """
    Test that non-empty DataFrame passes.
    """
    df = pd.DataFrame({'A': [1, 2, 3]})

    # Should not raise error
    validate_non_empty_dataframe(df, name="df")


def test_validate_non_empty_dataframe_invalid():
    """
    Test that empty DataFrame raises error.
    """
    df = pd.DataFrame()

    with pytest.raises(ValueError):
        validate_non_empty_dataframe(df, name="df")


def test_validate_ratio_type_error():
    """
    Test that non-numeric ratio raises TypeError.
    """
    with pytest.raises(TypeError):
        validate_ratio("0.5", name="ratio", inclusive=False)


def test_validate_columns_none():
    """
    Test that None columns parameter is accepted.

    When columns=None, validation should be skipped.
    """
    df = pd.DataFrame({'A': [1, 2]})

    # Should not raise error
    validate_columns_exist(df, None, name="df")


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

    test_validate_dataframe_valid()
    test_validate_dataframe_invalid()
    test_validate_columns_exist_valid()
    test_validate_columns_exist_missing()
    test_validate_time_column_valid()
    test_validate_time_column_invalid()
    test_validate_time_no_nat_valid()
    test_validate_time_no_nat_invalid()
    test_validate_ratio_valid()
    test_validate_ratio_invalid()
    test_validate_alpha_valid()
    test_validate_alpha_invalid()
    test_validate_timedelta_string_valid()
    test_validate_timedelta_string_invalid()
    test_validate_non_empty_dataframe_valid()
    test_validate_non_empty_dataframe_invalid()
    test_validate_ratio_type_error()
    test_validate_columns_none()