from __future__ import annotations

import numpy as np
import pytest

from cognitive_signal.models.types import SignalVector, TemporalSignal
from cognitive_signal.temporal.analyzer import TemporalAnalyzer


def _signal(
    *,
    valence: float = 0.0,
    arousal: float = 0.0,
    agency: float = 0.0,
    self_focus: float = 0.0,
    social_orientation: float = 0.0,
    action_ratio: float = 0.0,
) -> SignalVector:
    return SignalVector(
        embedding=np.zeros(384, dtype=np.float32),
        valence=valence,
        arousal=arousal,
        agency=agency,
        self_focus=self_focus,
        social_orientation=social_orientation,
        action_ratio=action_ratio,
    )


def test_compute_deltas_basic() -> None:
    analyzer = TemporalAnalyzer()
    result = analyzer.compute_deltas([0.1, 0.2, 0.15])
    assert result == pytest.approx([0.0, 0.1, -0.05], abs=1e-9)


def test_constant_signal_temporal_characteristics() -> None:
    analyzer = TemporalAnalyzer()
    series = [0.5] * 10
    assert analyzer.compute_trend(series) == pytest.approx(0.0, abs=1e-9)
    assert analyzer.compute_volatility(series) == pytest.approx(0.0, abs=1e-9)
    assert analyzer.compute_persistence(series) == pytest.approx(1.0, abs=1e-9)


def test_linear_increasing_signal_temporal_characteristics() -> None:
    analyzer = TemporalAnalyzer()
    series = [value / 10.0 for value in range(1, 11)]
    assert analyzer.compute_trend(series) > 0.0
    assert analyzer.compute_persistence(series) > 0.9


def test_random_oscillating_signal_temporal_characteristics() -> None:
    analyzer = TemporalAnalyzer()
    series = [0.1, 0.9, 0.2, 0.8, 0.15, 0.85, 0.05, 0.95]
    assert analyzer.compute_volatility(series) > 0.2
    assert analyzer.compute_persistence(series) < 0.3


def test_window_edge_cases_smaller_than_window_size() -> None:
    analyzer = TemporalAnalyzer()
    series = [0.4, 0.45, 0.35]
    output = analyzer.analyze_signal(series, window=7)
    assert len(output) == len(series)
    assert output[0].delta == pytest.approx(0.0, abs=1e-9)
    assert output[0].persistence == pytest.approx(1.0, abs=1e-9)


def test_analyze_signal_returns_temporal_signals_per_timestep() -> None:
    analyzer = TemporalAnalyzer()
    values = [0.2, 0.3, 0.5, 0.4, 0.45]
    output = analyzer.analyze_signal(values, window=3)
    assert len(output) == len(values)
    assert all(isinstance(item, TemporalSignal) for item in output)


def test_analyze_all_returns_all_scalar_signal_keys() -> None:
    analyzer = TemporalAnalyzer()
    signal_vectors = [
        _signal(
            valence=0.1,
            arousal=0.2,
            agency=0.3,
            self_focus=0.4,
            social_orientation=0.5,
            action_ratio=0.6,
        ),
        _signal(
            valence=0.2,
            arousal=0.3,
            agency=0.4,
            self_focus=0.5,
            social_orientation=0.6,
            action_ratio=0.7,
        ),
        _signal(
            valence=0.3,
            arousal=0.4,
            agency=0.5,
            self_focus=0.6,
            social_orientation=0.7,
            action_ratio=0.8,
        ),
        _signal(
            valence=0.4,
            arousal=0.5,
            agency=0.6,
            self_focus=0.7,
            social_orientation=0.8,
            action_ratio=0.9,
        ),
        _signal(
            valence=0.5,
            arousal=0.6,
            agency=0.7,
            self_focus=0.8,
            social_orientation=0.9,
            action_ratio=1.0,
        ),
    ]
    output = analyzer.analyze_all(signal_vectors, window=3)
    expected_keys = {
        "valence",
        "arousal",
        "agency",
        "self_focus",
        "social_orientation",
        "action_ratio",
    }
    assert set(output.keys()) == expected_keys
    for key in expected_keys:
        assert len(output[key]) == len(signal_vectors)
        assert output[key][0].delta == pytest.approx(0.0, abs=1e-9)


def test_empty_inputs() -> None:
    analyzer = TemporalAnalyzer()
    assert analyzer.compute_deltas([]) == []
    assert analyzer.analyze_signal([]) == []
    assert analyzer.analyze_all([]) == {
        "valence": [],
        "arousal": [],
        "agency": [],
        "self_focus": [],
        "social_orientation": [],
        "action_ratio": [],
    }
