"""
Tests for Decomposition.get_rgs() — issue #8.

Verifies that get_rgs() never returns None: when Guinier fitting fails
(sv.Rg is None), float('nan') must be returned instead.
"""
import math
import pytest
from unittest.mock import MagicMock, patch
from molass import get_version
get_version(toml_only=True)     # to ensure that the current repository is used


def make_guinier_stub(rg_value):
    """Return a minimal Guinier object stub with Rg set to *rg_value*."""
    stub = MagicMock()
    stub.Rg = rg_value
    return stub


def test_010_get_rgs_returns_nan_when_guinier_fails():
    """get_rgs() must return float('nan') instead of None on Guinier failure."""
    from molass.LowRank.Decomposition import Decomposition

    decomp = Decomposition.__new__(Decomposition)
    decomp.guinier_objects = [
        make_guinier_stub(12.5),   # successful fit
        make_guinier_stub(None),   # failed fit
    ]

    rgs = decomp.get_rgs()

    assert rgs[0] == pytest.approx(12.5)
    assert math.isnan(rgs[1]), "Expected float('nan') for failed Guinier, got: {!r}".format(rgs[1])


def test_020_get_rgs_no_none_in_result():
    """get_rgs() result must never contain None."""
    from molass.LowRank.Decomposition import Decomposition

    decomp = Decomposition.__new__(Decomposition)
    decomp.guinier_objects = [make_guinier_stub(v) for v in (15.0, None, 8.3, None)]

    rgs = decomp.get_rgs()

    assert None not in rgs, "get_rgs() must not return None values"
    assert all(isinstance(v, float) for v in rgs), "All values must be float"


def test_030_get_rgs_all_valid():
    """get_rgs() passes through valid Rg values unchanged."""
    from molass.LowRank.Decomposition import Decomposition

    decomp = Decomposition.__new__(Decomposition)
    decomp.guinier_objects = [make_guinier_stub(v) for v in (10.0, 20.0)]

    rgs = decomp.get_rgs()

    assert rgs == pytest.approx([10.0, 20.0])


def test_040_get_rg_curve_returns_rgcurve():
    """get_rg_curve() must return an RgCurve with .x, .y, .scores attributes."""
    from molass_data import SAMPLE1
    from molass.DataObjects import SecSaxsData as SSD
    from molass.Guinier.RgCurve import RgCurve
    import numpy as np

    ssd = SSD(SAMPLE1)
    decomp = ssd.quick_decomposition()
    rgcurve = decomp.get_rg_curve()

    assert isinstance(rgcurve, RgCurve), f"Expected RgCurve, got {type(rgcurve)}"
    assert len(rgcurve.x) > 0, "RgCurve.x should be non-empty"
    assert len(rgcurve.y) == len(rgcurve.x), "RgCurve.x and .y must have equal length"
    assert len(rgcurve.scores) == len(rgcurve.x), "RgCurve.scores must match .x length"
    # Rg values: None for failed fits, float (possibly negative) for attempted fits.
    # With ADD_ALL_RESULTS=True, every frame is included.
    # At least some frames should have a non-None result.
    valid_rgs = [v for v in rgcurve.y if v is not None]
    assert len(valid_rgs) > 0, "At least some Rg values should be non-None"
