"""Tests for boundary data utilities."""

import numpy as np
import pytest

from harmonic_measure.boundary_data import (
    sigmoid,
    smooth_indicator,
    smooth_corner_indicator,
    corner_boundary_data,
)


class TestSigmoid:
    """Tests for sigmoid function."""

    def test_sigmoid_at_zero(self):
        """σ(0) = 0.5"""
        assert sigmoid(0.0) == pytest.approx(0.5)

    def test_sigmoid_limits(self):
        """σ(-∞) → 0, σ(∞) → 1"""
        assert sigmoid(-10) < 0.001
        assert sigmoid(10) > 0.999

    def test_sigmoid_symmetry(self):
        """σ(t) + σ(-t) = 1"""
        for t in [0.5, 1.0, 2.0, 5.0]:
            assert sigmoid(t) + sigmoid(-t) == pytest.approx(1.0)

    def test_sigmoid_vectorized(self):
        """Should work on arrays."""
        t = np.array([-5.0, 0.0, 5.0])
        result = sigmoid(t)

        assert result.shape == (3,)
        assert result[0] < 0.01
        assert result[1] == pytest.approx(0.5)
        assert result[2] > 0.99


class TestSmoothIndicator:
    """Tests for smooth indicator function."""

    def test_inside_returns_high(self):
        """Inside=True should return value near 1."""
        val = smooth_indicator(True, delta=0.1)
        assert val > 0.9

    def test_outside_returns_low(self):
        """Inside=False should return value near 0."""
        val = smooth_indicator(False, delta=0.1)
        assert val < 0.1

    def test_delta_affects_sharpness(self):
        """Smaller delta should give values closer to 0/1."""
        val_sharp = smooth_indicator(True, delta=0.01)
        val_soft = smooth_indicator(True, delta=1.0)

        # Sharp should be closer to 1
        assert val_sharp > val_soft


class TestSmoothCornerIndicator:
    """Tests for smooth corner indicator function."""

    def test_output_shape(self):
        """Output should match number of input points."""
        pts = np.random.randn(50, 2)
        corners = [(1.0, 0.0), (-1.0, 0.0)]

        g = smooth_corner_indicator(pts, corners, radius=0.5)

        assert g.shape == (50,)

    def test_values_in_range(self):
        """Values should be in [0, 1]."""
        pts = np.random.randn(100, 2) * 2  # Some near corners, some far
        corners = [(1.0, 1.0), (-1.0, -1.0)]

        g = smooth_corner_indicator(pts, corners, radius=0.5)

        assert np.all(g >= 0)
        assert np.all(g <= 1)

    def test_point_at_corner(self):
        """Point exactly at corner should have high value."""
        corners = [(1.0, 0.0), (-1.0, 0.0)]
        pts = np.array([[1.0, 0.0]])  # Exactly at corner

        g = smooth_corner_indicator(pts, corners, radius=0.5)

        assert g[0] > 0.9

    def test_point_far_from_corners(self):
        """Point far from corners should have low value."""
        corners = [(1.0, 0.0), (-1.0, 0.0)]
        pts = np.array([[0.0, 10.0]])  # Far from any corner

        g = smooth_corner_indicator(pts, corners, radius=0.5)

        assert g[0] < 0.1

    def test_custom_delta(self):
        """Custom delta should be used."""
        corners = [(1.0, 0.0)]
        pts = np.array([[0.9, 0.0]])  # Just inside radius

        g_auto = smooth_corner_indicator(pts, corners, radius=0.5)
        g_custom = smooth_corner_indicator(pts, corners, radius=0.5, delta=0.01)

        # Both should give reasonable values
        assert g_auto > 0.5
        assert g_custom > 0.5


class TestCornerBoundaryData:
    """Tests for standard corner boundary data."""

    def test_output_shape(self):
        """Output should match number of points."""
        pts = np.random.randn(64, 2)

        g = corner_boundary_data(pts, a=1.0, rho=0.5)

        assert g.shape == (64,)

    def test_values_in_range(self):
        """Values should be in [0, 1]."""
        pts = np.random.randn(100, 2) * 2

        g = corner_boundary_data(pts, a=1.0, rho=0.5)

        assert np.all(g >= 0)
        assert np.all(g <= 1)

    def test_corners_at_expected_locations(self):
        """Corners should be at (±1, ±a)."""
        a = 2.0
        corners = [
            [1.0, a], [1.0, -a], [-1.0, a], [-1.0, -a]
        ]
        pts = np.array(corners)

        g = corner_boundary_data(pts, a=a, rho=0.5)

        # All corner points should have high indicator
        assert np.all(g > 0.9)

    def test_custom_delta(self):
        """Custom delta should work."""
        pts = np.array([[1.0, 1.0]])

        g = corner_boundary_data(pts, a=1.0, rho=0.5, delta=0.01)

        assert g[0] > 0.9

    def test_edge_midpoints_low(self):
        """Points on edges (not corners) should have low indicator."""
        # Mid-points of edges for a=1
        edge_pts = np.array([
            [0.0, 1.0],   # Top edge midpoint
            [0.0, -1.0],  # Bottom edge midpoint
            [1.0, 0.0],   # Right edge midpoint
            [-1.0, 0.0],  # Left edge midpoint
        ])

        g = corner_boundary_data(edge_pts, a=1.0, rho=0.3)

        # Edge midpoints should be far from corners
        assert np.all(g < 0.1)
