"""
Test module for the filters module.

Authors: Sebastián Rodríguez-Martínez
Contact: srodriguez@mbari.org
Last Update: 2024-09-20
"""

import numpy as np
import pytest

from navlib import filter_lowpass, filter_median, filter_savgol
from navlib.math import filter_lowpass as filter_lowpass_relative
from navlib.math import filter_median as filter_median_relative
from navlib.math import filter_savgol as filter_savgol_relative


def test_imports():
    # Checks that the absolute and relative imports work correctly
    assert filter_lowpass_relative.__code__.co_code == filter_lowpass.__code__.co_code
    assert filter_median_relative.__code__.co_code == filter_median.__code__.co_code
    assert filter_savgol_relative.__code__.co_code == filter_savgol.__code__.co_code


def test_filter_median():
    """
    Test for the filter_median function.
    """
    # Test 1: Invalid data type (string)
    with pytest.raises(TypeError):
        filter_median("invalid")

    # Test 2: Invalid time type (string)
    with pytest.raises(TypeError):
        filter_median([1, 2, 3], "invalid")

    # Test 3: Invalid window type (string)
    with pytest.raises(TypeError):
        filter_median([1, 2, 3], window="invalid")

    # Test 4: Invalid threshold type (string)
    with pytest.raises(TypeError):
        filter_median([1, 2, 3], threshold="invalid")

    # Test 5: Invalid patch type (string)
    with pytest.raises(TypeError):
        filter_median([1, 2, 3], patch="invalid")

    # Test 6: Invalid remove_outliers type (string)
    with pytest.raises(TypeError):
        filter_median([1, 2, 3], remove_outliers="invalid")

    # Test 7: Window less than or equal to zero
    with pytest.raises(ValueError):
        filter_median([1, 2, 3], window=0)

    # Test 8: Threshold less than or equal to zero
    with pytest.raises(ValueError):
        filter_median([1, 2, 3], threshold=0)

    # Test 9: Time not a 1D array
    with pytest.raises(ValueError):
        filter_median([1, 2, 3], time=[[1, 2], [3, 4]])

    # Test 10: Time and data length mismatch
    with pytest.raises(ValueError):
        filter_median([1, 2, 3], time=[1, 2])

    # Test 11: Valid input (basic functionality check)
    data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    filtered_data, filtered_time = filter_median(data, window=3)
    assert isinstance(filtered_data, np.ndarray)
    assert filtered_time is None
    assert filtered_data.shape == data.shape

    # Test 12: Valid input (basic functionality check)
    data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    time = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    filtered_data, filtered_time = filter_median(data, time=time, window=2)
    assert isinstance(filtered_data, np.ndarray)
    assert isinstance(filtered_time, np.ndarray)
    assert filtered_data.shape == data.shape
    assert filtered_time.shape == time.shape


def test_filter_savgol():
    """
    Test for the filter_savgol function.
    """
    # Test 1: Invalid data type (string)
    with pytest.raises(TypeError):
        filter_savgol("invalid")

    # Test 2: Invalid time type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], "invalid")

    # Test 3: Invalid window type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], window="invalid")

    # Test 4: Invalid polynomial type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], polynomial="invalid")

    # Test 5: Invalid threshold type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], threshold="invalid")

    # Test 6: Invalid patch type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], patch="invalid")

    # Test 7: Invalid remove_outliers type (string)
    with pytest.raises(TypeError):
        filter_savgol([1, 2, 3], remove_outliers="invalid")

    # Test 8: Window less than or equal to zero
    with pytest.raises(ValueError):
        filter_savgol([1, 2, 3], window=0)

    # Test 9: Polynomial less than or equal to zero
    with pytest.raises(ValueError):
        filter_savgol([1, 2, 3], polynomial=0)

    # Test 10: Threshold less than or equal to zero
    with pytest.raises(ValueError):
        filter_savgol([1, 2, 3], threshold=0)

    # Test 11: Time not a 1D array
    with pytest.raises(ValueError):
        filter_savgol([1, 2, 3], time=[[1, 2], [3, 4]])

    # Test 12: Time and data length mismatch
    with pytest.raises(ValueError):
        filter_savgol([1, 2, 3], time=[1, 2])

    # Test 13: Valid input (basic functionality check)
    data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    filtered_data, filtered_time = filter_savgol(data, window=3)
    assert isinstance(filtered_data, np.ndarray)
    assert filtered_time is None
    assert filtered_data.shape == data.shape

    # Test 14: Valid input (basic functionality check)
    data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    time = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    filtered_data, filtered_time = filter_savgol(data, time=time, window=2)
    assert isinstance(filtered_data, np.ndarray)
    assert isinstance(filtered_time, np.ndarray)
    assert filtered_data.shape == data.shape
    assert filtered_time.shape == time.shape


def test_filter_lowpass():
    """
    Test for the filter_lowpass function.
    """
    # Test 1: Invalid data type (string)
    with pytest.raises(TypeError):
        filter_lowpass("invalid")

    # Test 2: Invalid sample frequency type (string)
    with pytest.raises(TypeError):
        filter_lowpass([1, 2, 3], sample_freq_hz="invalid")

    # Test 3: Invalid cutoff frequency type (string)
    with pytest.raises(TypeError):
        filter_lowpass([1, 2, 3], cutoff_freq_hz="invalid")

    # Test 4: Invalid filter order type (string)
    with pytest.raises(TypeError):
        filter_lowpass([1, 2, 3], filter_order="invalid")

    # Test 5: Invalid causality type (integer)
    with pytest.raises(TypeError):
        filter_lowpass([1, 2, 3], causality=123)

    # Test 6: Sample frequency less than or equal to zero
    with pytest.raises(ValueError):
        filter_lowpass([1, 2, 3], sample_freq_hz=0)

    # Test 7: Cutoff frequency less than or equal to zero
    with pytest.raises(ValueError):
        filter_lowpass([1, 2, 3], cutoff_freq_hz=0)

    # Test 8: Filter order less than or equal to zero
    with pytest.raises(ValueError):
        filter_lowpass([1, 2, 3], filter_order=0)

    # Test 9: Invalid causality value
    with pytest.raises(ValueError):
        filter_lowpass([1, 2, 3], causality="invalid")

    # Test 10: Cutoff frequency exceeds half the sample frequency
    with pytest.raises(ValueError):
        filter_lowpass([1, 2, 3], sample_freq_hz=1.0, cutoff_freq_hz=0.6)

    # Test 11: Valid input (basic functionality check)
    data = np.arange(20)
    filtered_data = filter_lowpass(data, causality="causal")
    assert isinstance(filtered_data, np.ndarray)
    assert filtered_data.shape == data.shape

    # Test 12: Valid input (basic functionality check)
    data = np.arange(20)
    filtered_data = filter_lowpass(data, causality="noncausal")
    assert isinstance(filtered_data, np.ndarray)
    assert filtered_data.shape == data.shape
