"""
Tests for vectorization methods.
"""

import pytest
import numpy as np
from medtda import vectorizers


@pytest.fixture
def sample_barcode():
    """Create a sample persistence barcode."""
    return np.array([
        [0.0, 1.0],
        [0.5, 2.0],
        [1.0, 3.0],
        [1.5, 2.5],
    ])


@pytest.fixture
def empty_barcode():
    """Create an empty barcode."""
    return np.array([]).reshape(0, 2)


class TestBettiCurve:
    """Tests for Betti curve vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test Betti curve with valid barcode."""
        result = vectorizers.betti_curve(sample_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test Betti curve with empty barcode."""
        result = vectorizers.betti_curve(empty_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(result == 0)


class TestEntropySummary:
    """Tests for entropy summary vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test entropy summary with valid barcode."""
        result = vectorizers.entropy_summary(sample_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test entropy summary with empty barcode."""
        result = vectorizers.entropy_summary(empty_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(result == 0)


class TestPersistenceImage:
    """Tests for persistence image vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test persistence image with valid barcode."""
        result = vectorizers.persistence_image(sample_barcode, bandwidth=0.2, resolution=10)
        assert result.shape == (100,)  # 10x10 flattened
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test persistence image with empty barcode."""
        result = vectorizers.persistence_image(empty_barcode, resolution=10)
        assert result.shape == (100,)
        assert np.all(result == 0)


class TestPersistenceLandscape:
    """Tests for persistence landscape vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test persistence landscape with valid barcode."""
        result = vectorizers.persistence_landscape(sample_barcode, resolution=50, num_landscapes=3)
        assert result.shape == (150,)  # 50 * 3
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test persistence landscape with empty barcode."""
        result = vectorizers.persistence_landscape(empty_barcode, resolution=50, num_landscapes=3)
        assert result.shape == (150,)
        assert np.all(result == 0)


class TestPersistenceLifespan:
    """Tests for persistence lifespan vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test persistence lifespan with valid barcode."""
        result = vectorizers.persistence_lifespan(sample_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test persistence lifespan with empty barcode."""
        result = vectorizers.persistence_lifespan(empty_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(result == 0)


class TestPersistenceSilhouette:
    """Tests for persistence silhouette vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test persistence silhouette with valid barcode."""
        result = vectorizers.persistence_silhouette(sample_barcode, resolution=50, weight=1)
        assert result.shape == (50,)
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test persistence silhouette with empty barcode."""
        result = vectorizers.persistence_silhouette(empty_barcode, resolution=50)
        assert result.shape == (50,)
        assert np.all(result == 0)


class TestPersistenceStats:
    """Tests for persistence statistics vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test persistence stats with valid barcode."""
        result = vectorizers.persistence_stats(sample_barcode)
        assert isinstance(result, dict)
        assert len(result) == 38  # 38 statistical features
        assert all(np.isfinite(v) for v in result.values())

    def test_with_empty_barcode(self, empty_barcode):
        """Test persistence stats with empty barcode."""
        result = vectorizers.persistence_stats(empty_barcode)
        assert isinstance(result, dict)
        assert len(result) == 38
        assert all(v == 0 for v in result.values())

    def test_statistics_are_meaningful(self, sample_barcode):
        """Test that computed statistics make sense."""
        result = vectorizers.persistence_stats(sample_barcode)
        # Check that bar count is correct
        assert result['count'] == len(sample_barcode)


class TestPersistenceTropicalCoordinates:
    """Tests for persistence tropical coordinates vectorization."""

    def test_with_valid_barcode(self, sample_barcode):
        """Test tropical coordinates with valid barcode."""
        result = vectorizers.persistence_tropical_coordinates(sample_barcode, r=20)
        assert result.shape == (7,)
        assert np.all(np.isfinite(result))

    def test_with_empty_barcode(self, empty_barcode):
        """Test tropical coordinates with empty barcode."""
        result = vectorizers.persistence_tropical_coordinates(empty_barcode)
        assert result.shape == (7,)
        assert np.all(result == 0)


class TestVectorizationMethodsDict:
    """Tests for VECTORIZATION_METHODS dictionary."""

    def test_all_methods_present(self):
        """Test that all methods are in the dictionary."""
        expected_methods = {
            # Snake_case names
            'betti_curve',
            'entropy_summary',
            'persistence_image',
            'persistence_landscape',
            'persistence_lifespan',
            'persistence_silhouette',
            'persistence_stats',
            'persistence_tropical_coordinates',
            # PascalCase aliases for backward compatibility
            'BettiCurve',
            'EntropySummary',
            'PersImage',
            'PersLandscape',
            'PersLifespan',
            'PersSilhouette',
            'PersStats',
            'PersTropicalCoordinates',
        }
        assert set(vectorizers.VECTORIZATION_METHODS.keys()) == expected_methods

    def test_methods_are_callable(self):
        """Test that all methods in dictionary are callable."""
        for method_name, method_func in vectorizers.VECTORIZATION_METHODS.items():
            assert callable(method_func), f"{method_name} is not callable"

    def test_aliases_point_to_same_functions(self):
        """Test that PascalCase aliases point to the same functions as snake_case names."""
        assert vectorizers.VECTORIZATION_METHODS['betti_curve'] is vectorizers.VECTORIZATION_METHODS['BettiCurve']
        assert vectorizers.VECTORIZATION_METHODS['persistence_stats'] is vectorizers.VECTORIZATION_METHODS['PersStats']
        assert vectorizers.VECTORIZATION_METHODS['persistence_image'] is vectorizers.VECTORIZATION_METHODS['PersImage']
        assert vectorizers.VECTORIZATION_METHODS['persistence_landscape'] is vectorizers.VECTORIZATION_METHODS['PersLandscape']
