"""Tests for the pure-SVG waveform plot generation module."""

import numpy as np
import pytest

from mcltspice.svg_plot import (
    _format_freq,
    _nice_ticks,
    plot_bode,
    plot_spectrum,
    plot_timeseries,
)

# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture()
def sine_wave():
    """A 1 kHz sine wave sampled at 100 kHz for 10 ms."""
    t = np.linspace(0, 0.01, 1000, endpoint=False)
    v = np.sin(2 * np.pi * 1000 * t)
    return t, v


@pytest.fixture()
def bode_data():
    """Simple first-order lowpass Bode response (fc = 1 kHz)."""
    freq = np.logspace(1, 6, 500)
    fc = 1e3
    mag_db = -10 * np.log10(1 + (freq / fc) ** 2)
    phase_deg = -np.degrees(np.arctan(freq / fc))
    return freq, mag_db, phase_deg


@pytest.fixture()
def spectrum_data():
    """Synthetic FFT spectrum with a peak at 1 kHz."""
    freq = np.logspace(1, 5, 300)
    mag_db = -60 * np.ones_like(freq)
    peak_idx = np.argmin(np.abs(freq - 1e3))
    mag_db[peak_idx] = 0.0
    return freq, mag_db


# ---------------------------------------------------------------------------
# Timeseries
# ---------------------------------------------------------------------------


class TestPlotTimeseries:
    def test_basic(self, sine_wave):
        """A simple sine wave produces a valid SVG with expected elements."""
        t, v = sine_wave
        svg = plot_timeseries(t, v)
        assert svg.startswith("<svg")
        assert "<path" in svg
        assert "Time Domain" in svg

    def test_empty_arrays(self):
        """Empty input should not crash and should return a valid SVG."""
        svg = plot_timeseries([], [])
        assert svg.startswith("<svg")
        assert "</svg>" in svg

    def test_custom_title_and_labels(self, sine_wave):
        """Custom title and ylabel should appear in the SVG output."""
        t, v = sine_wave
        svg = plot_timeseries(t, v, title="My Signal", ylabel="Current (A)")
        assert "My Signal" in svg
        assert "Current (A)" in svg

    def test_svg_dimensions(self, sine_wave):
        """The width/height attributes should match the requested size."""
        t, v = sine_wave
        svg = plot_timeseries(t, v, width=1024, height=768)
        assert 'width="1024"' in svg
        assert 'height="768"' in svg


# ---------------------------------------------------------------------------
# Bode
# ---------------------------------------------------------------------------


class TestPlotBode:
    def test_magnitude_only(self, bode_data):
        """Bode plot without phase produces a valid SVG with one trace."""
        freq, mag_db, _ = bode_data
        svg = plot_bode(freq, mag_db)
        assert svg.startswith("<svg")
        assert "<path" in svg
        assert "Bode Plot" in svg

    def test_with_phase(self, bode_data):
        """Bode plot with phase should contain two <path> elements (mag + phase)."""
        freq, mag_db, phase_deg = bode_data
        svg = plot_bode(freq, mag_db, phase_deg)
        assert svg.startswith("<svg")
        # Two traces -- magnitude and phase
        assert svg.count("<path") >= 2
        # Phase subplot label
        assert "Phase (deg)" in svg

    def test_log_axis_ticks(self, bode_data):
        """Log frequency axis should contain tick labels at powers of 10."""
        freq, mag_db, _ = bode_data
        svg = plot_bode(freq, mag_db)
        # Expect at least some frequency labels like "100", "1k", "10k", "100k"
        found = sum(1 for lbl in ("100", "1k", "10k", "100k") if lbl in svg)
        assert found >= 2, f"Expected log tick labels in SVG; found {found}"


# ---------------------------------------------------------------------------
# Spectrum
# ---------------------------------------------------------------------------


class TestPlotSpectrum:
    def test_basic(self, spectrum_data):
        """A simple spectrum produces a valid SVG."""
        freq, mag_db = spectrum_data
        svg = plot_spectrum(freq, mag_db)
        assert svg.startswith("<svg")
        assert "<path" in svg
        assert "FFT Spectrum" in svg


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


class TestNiceTicks:
    def test_simple_range(self):
        ticks = _nice_ticks(0, 10, n_ticks=5)
        assert len(ticks) >= 3
        assert ticks[0] <= 0
        assert ticks[-1] >= 10

    def test_equal_values(self):
        """When vmin == vmax, return a single-element list."""
        ticks = _nice_ticks(5, 5)
        assert ticks == [5]

    def test_negative_range(self):
        ticks = _nice_ticks(-100, -20, n_ticks=5)
        assert ticks[0] <= -100
        assert ticks[-1] >= -20

    def test_small_range(self):
        ticks = _nice_ticks(0.001, 0.005, n_ticks=5)
        assert all(0 <= t <= 0.01 for t in ticks)


class TestFormatFreq:
    def test_hz(self):
        assert _format_freq(1) == "1"
        assert _format_freq(10) == "10"
        assert _format_freq(100) == "100"

    def test_khz(self):
        assert _format_freq(1000) == "1k"
        assert _format_freq(10000) == "10k"
        assert _format_freq(100000) == "100k"

    def test_mhz(self):
        assert _format_freq(1e6) == "1M"
        assert _format_freq(10e6) == "10M"

    def test_ghz(self):
        assert _format_freq(1e9) == "1G"

    def test_zero(self):
        assert _format_freq(0) == "0"


class TestSvgDimensions:
    def test_timeseries_dimensions(self):
        t = np.linspace(0, 1, 100)
        v = np.sin(t)
        svg = plot_timeseries(t, v, width=640, height=480)
        assert 'width="640"' in svg
        assert 'height="480"' in svg

    def test_bode_dimensions(self):
        freq = np.logspace(1, 5, 50)
        mag = np.zeros(50)
        svg = plot_bode(freq, mag, width=900, height=600)
        assert 'width="900"' in svg
        assert 'height="600"' in svg

    def test_spectrum_dimensions(self):
        freq = np.logspace(1, 5, 50)
        mag = np.zeros(50)
        svg = plot_spectrum(freq, mag, width=1000, height=500)
        assert 'width="1000"' in svg
        assert 'height="500"' in svg
