"""Behavioral tests for the public acfmperiod API."""

from importlib.resources import as_file, files

import numpy as np

from acfmperiod import (
    CovCorMPer,
    CovCorPer,
    CrossPeriodogram,
    MCrossPeriodogram,
    MPerACF,
    MPerioReg,
    PerACF,
    PerioReg,
)


def test_perio_reg_length_and_symmetry():
    """PerioReg should return a mirrored spectrum of length n-1."""

    x = np.arange(1, 9, dtype=float)
    p = PerioReg(x)
    expected_shape = (len(x) - 1,)
    assert p.shape == expected_shape, f"PerioReg shape: expected {expected_shape}, got {p.shape}"
    np.testing.assert_allclose(
        p,
        p[::-1],
        rtol=1e-10,
        atol=1e-10,
        err_msg="PerioReg output should be symmetric around the center frequency.",
    )


def test_m_perio_reg_length_and_symmetry():
    """MPerioReg should preserve the same mirrored layout."""

    x = np.linspace(0.0, 1.0, 10)
    p = MPerioReg(x)
    expected_shape = (len(x) - 1,)
    assert p.shape == expected_shape, f"MPerioReg shape: expected {expected_shape}, got {p.shape}"
    np.testing.assert_allclose(
        p,
        p[::-1],
        rtol=1e-10,
        atol=1e-10,
        err_msg="MPerioReg output should be symmetric around the center frequency.",
    )


def test_cross_periodogram_dimensions():
    """Cross-periodogram outputs should match the input length."""

    x = np.arange(1, 13, dtype=float)
    y = np.arange(2, 14, dtype=float)

    c = CrossPeriodogram(x, y)
    expected_shape = (len(x),)
    assert (
        c.crossperiodxy.shape == expected_shape
    ), f"CrossPeriodogram crossperiodxy shape: expected {expected_shape}, got {c.crossperiodxy.shape}"
    assert (
        c.crossperiodyx.shape == expected_shape
    ), f"CrossPeriodogram crossperiodyx shape: expected {expected_shape}, got {c.crossperiodyx.shape}"

    m = MCrossPeriodogram(x, y)
    assert (
        m.crossperiodxy.shape == expected_shape
    ), f"MCrossPeriodogram crossperiodxy shape: expected {expected_shape}, got {m.crossperiodxy.shape}"
    assert (
        m.crossperiodyx.shape == expected_shape
    ), f"MCrossPeriodogram crossperiodyx shape: expected {expected_shape}, got {m.crossperiodyx.shape}"


def test_per_acf_shapes_and_cov_cor_wrappers():
    """PerACF and CovCorPer should produce expected matrix/tensor shapes."""

    rng = np.random.default_rng(42)
    x = rng.normal(size=(64, 2))

    res = PerACF(x, lagmax=12, doplot=False)
    expected_lag_shape = (12, 2, 2)
    assert res.acf.shape == expected_lag_shape, f"PerACF acf shape: expected {expected_lag_shape}, got {res.acf.shape}"
    assert res.lag.shape == expected_lag_shape, f"PerACF lag shape: expected {expected_lag_shape}, got {res.lag.shape}"

    corr0 = CovCorPer(x, typevalue="correlation")
    cov0 = CovCorPer(x, typevalue="covariance")
    expected_matrix_shape = (2, 2)
    assert corr0.shape == expected_matrix_shape, f"CovCorPer correlation shape: expected {expected_matrix_shape}, got {corr0.shape}"
    assert cov0.shape == expected_matrix_shape, f"CovCorPer covariance shape: expected {expected_matrix_shape}, got {cov0.shape}"
    np.testing.assert_allclose(
        np.diag(corr0),
        np.ones(2),
        atol=1e-8,
        rtol=1e-8,
        err_msg="CovCorPer correlation diagonal should be all ones.",
    )


def test_m_per_acf_shapes_and_cov_cor_wrappers():
    """MPerACF and CovCorMPer should produce expected matrix/tensor shapes."""

    rng = np.random.default_rng(123)
    x = rng.normal(size=(64, 3))

    res = MPerACF(x, lagmax=10, doplot=False)
    expected_lag_shape = (10, 3, 3)
    assert res.acf.shape == expected_lag_shape, f"MPerACF acf shape: expected {expected_lag_shape}, got {res.acf.shape}"
    assert res.lag.shape == expected_lag_shape, f"MPerACF lag shape: expected {expected_lag_shape}, got {res.lag.shape}"

    corr0 = CovCorMPer(x, typevalue="correlation")
    cov0 = CovCorMPer(x, typevalue="covariance")
    expected_matrix_shape = (3, 3)
    assert corr0.shape == expected_matrix_shape, f"CovCorMPer correlation shape: expected {expected_matrix_shape}, got {corr0.shape}"
    assert cov0.shape == expected_matrix_shape, f"CovCorMPer covariance shape: expected {expected_matrix_shape}, got {cov0.shape}"
    np.testing.assert_allclose(
        np.diag(corr0),
        np.ones(3),
        atol=1e-8,
        rtol=1e-8,
        err_msg="CovCorMPer correlation diagonal should be all ones.",
    )


def test_pm10_dataset_prints_normal_and_robust_acf_and_acovf():
    """Packaged PM10 data should run through normal and robust ACF/ACOVF."""

    dataset_ref = files("acfmperiod.data").joinpath("pm10data.csv")
    with as_file(dataset_ref) as dataset_path:
        data = np.loadtxt(dataset_path, delimiter=",", skiprows=1)

    series = data[:, 0]
    lagmax = 8

    normal_acf = PerACF(series, lagmax=lagmax, typevalue="correlation", doplot=False).acf[:, 0, 0]
    normal_acovf = PerACF(series, lagmax=lagmax, typevalue="covariance", doplot=False).acf[:, 0, 0]
    robust_acf = MPerACF(series, lagmax=lagmax, typevalue="correlation", doplot=False).acf[:, 0, 0]
    robust_acovf = MPerACF(series, lagmax=lagmax, typevalue="covariance", doplot=False).acf[:, 0, 0]

    print("\nPM10 normal ACF:", np.array2string(normal_acf, precision=6, suppress_small=True))
    print("PM10 normal ACOVF:", np.array2string(normal_acovf, precision=6, suppress_small=True))
    print("PM10 robust ACF:", np.array2string(robust_acf, precision=6, suppress_small=True))
    print("PM10 robust ACOVF:", np.array2string(robust_acovf, precision=6, suppress_small=True))

    expected_shape = (lagmax,)
    for name, values in (
        ("normal ACF", normal_acf),
        ("normal ACOVF", normal_acovf),
        ("robust ACF", robust_acf),
        ("robust ACOVF", robust_acovf),
    ):
        assert values.shape == expected_shape, f"{name} shape: expected {expected_shape}, got {values.shape}"
        assert np.isfinite(values).all(), f"{name} contains non-finite values."

    np.testing.assert_allclose(normal_acf[0], 1.0, atol=1e-8, rtol=1e-8, err_msg="Normal ACF lag 0 should be 1.")
    np.testing.assert_allclose(robust_acf[0], 1.0, atol=1e-8, rtol=1e-8, err_msg="Robust ACF lag 0 should be 1.")
