"""Tests for power_analysis module: average power, efficiency, power factor."""

import numpy as np
import pytest

from mcltspice.power_analysis import (
    compute_average_power,
    compute_efficiency,
    compute_instantaneous_power,
    compute_power_metrics,
)


class TestComputeAveragePower:
    def test_dc_power(self):
        """DC: P = V * I exactly."""
        n = 1000
        t = np.linspace(0, 1.0, n)
        v = np.full(n, 5.0)
        i = np.full(n, 2.0)
        p = compute_average_power(t, v, i)
        assert p == pytest.approx(10.0, rel=1e-6)

    def test_ac_in_phase(self):
        """In-phase AC: P_avg = Vpk*Ipk/2."""
        n = 10000
        t = np.linspace(0, 0.01, n, endpoint=False)  # 1 full period at 100 Hz
        freq = 100.0
        Vpk = 10.0
        Ipk = 2.0
        v = Vpk * np.sin(2 * np.pi * freq * t)
        i = Ipk * np.sin(2 * np.pi * freq * t)
        p = compute_average_power(t, v, i)
        expected = Vpk * Ipk / 2.0  # = Vrms * Irms
        assert p == pytest.approx(expected, rel=0.02)

    def test_ac_quadrature(self):
        """90-degree phase shift: P_avg ~ 0 (reactive power only)."""
        n = 10000
        t = np.linspace(0, 0.01, n, endpoint=False)
        freq = 100.0
        v = np.sin(2 * np.pi * freq * t)
        i = np.cos(2 * np.pi * freq * t)  # 90 deg shifted
        p = compute_average_power(t, v, i)
        assert p == pytest.approx(0.0, abs=0.01)

    def test_short_signal(self):
        assert compute_average_power(np.array([0.0]), np.array([5.0]), np.array([2.0])) == 0.0


class TestComputeEfficiency:
    def test_known_efficiency(self):
        """Input 10W, output 8W -> 80% efficiency."""
        n = 1000
        t = np.linspace(0, 1.0, n)
        vin = np.full(n, 10.0)
        iin = np.full(n, 1.0)   # 10W input
        vout = np.full(n, 8.0)
        iout = np.full(n, 1.0)  # 8W output

        result = compute_efficiency(t, vin, iin, vout, iout)
        assert result["efficiency_percent"] == pytest.approx(80.0, rel=0.01)
        assert result["input_power_watts"] == pytest.approx(10.0, rel=0.01)
        assert result["output_power_watts"] == pytest.approx(8.0, rel=0.01)
        assert result["power_dissipated_watts"] == pytest.approx(2.0, rel=0.01)

    def test_zero_input_power(self):
        """Zero input -> 0% efficiency (avoid division by zero)."""
        n = 100
        t = np.linspace(0, 1.0, n)
        zeros = np.zeros(n)
        result = compute_efficiency(t, zeros, zeros, zeros, zeros)
        assert result["efficiency_percent"] == 0.0


class TestPowerFactor:
    def test_dc_power_factor(self):
        """DC signals (in phase) should have PF = 1.0."""
        n = 1000
        t = np.linspace(0, 1.0, n)
        v = np.full(n, 5.0)
        i = np.full(n, 2.0)
        result = compute_power_metrics(t, v, i)
        assert result["power_factor"] == pytest.approx(1.0, rel=0.01)

    def test_ac_in_phase_power_factor(self):
        """In-phase AC should have PF ~ 1.0."""
        n = 10000
        t = np.linspace(0, 0.01, n, endpoint=False)
        freq = 100.0
        v = np.sin(2 * np.pi * freq * t)
        i = np.sin(2 * np.pi * freq * t)
        result = compute_power_metrics(t, v, i)
        assert result["power_factor"] == pytest.approx(1.0, rel=0.05)

    def test_ac_quadrature_power_factor(self):
        """90-degree phase shift -> PF ~ 0."""
        n = 10000
        t = np.linspace(0, 0.01, n, endpoint=False)
        freq = 100.0
        v = np.sin(2 * np.pi * freq * t)
        i = np.cos(2 * np.pi * freq * t)
        result = compute_power_metrics(t, v, i)
        assert result["power_factor"] == pytest.approx(0.0, abs=0.05)

    def test_empty_signals(self):
        result = compute_power_metrics(np.array([]), np.array([]), np.array([]))
        assert result["power_factor"] == 0.0


class TestInstantaneousPower:
    def test_element_wise(self):
        v = np.array([1.0, 2.0, 3.0])
        i = np.array([0.5, 1.0, 1.5])
        p = compute_instantaneous_power(v, i)
        np.testing.assert_array_almost_equal(p, [0.5, 2.0, 4.5])

    def test_complex_uses_real(self):
        """Should use real parts only."""
        v = np.array([3.0 + 4j])
        i = np.array([2.0 + 1j])
        p = compute_instantaneous_power(v, i)
        assert p[0] == pytest.approx(6.0)  # 3 * 2
