"""Smoke tests for graphichelpers, visualizationhelpers, and commonhelpers (T2).

These tests verify that imports succeed and basic functions are callable
without triggering full rendering.  Matplotlib is switched to the 'Agg'
non-interactive backend so the tests run in headless CI environments.
"""
import matplotlib
matplotlib.use('Agg')  # must be set before any pyplot import

import pytest
import numpy as np
import pandas as pd


# ---------------------------------------------------------------------------
# commonhelpers
# ---------------------------------------------------------------------------

class TestCommonHelpers:

    def test_flatten_namevalue_pairs(self):
        from pwml.utilities.commonhelpers import flatten_namevalue_pairs
        pairs = [('Accuracy', 0.95, '.2f'), ('F1', 0.88, '.2f')]
        result = flatten_namevalue_pairs(pairs, separator=', ')
        assert 'Accuracy' in result
        assert '0.95' in result
        assert 'F1' in result

    def test_shift_array_positive(self):
        from pwml.utilities.commonhelpers import shift_array
        arr = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
        shifted = shift_array(arr, 2)
        assert np.isnan(shifted[0])
        assert np.isnan(shifted[1])
        assert shifted[2] == 1.0

    def test_shift_array_negative(self):
        from pwml.utilities.commonhelpers import shift_array
        arr = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
        shifted = shift_array(arr, -1)
        assert shifted[0] == 2.0
        assert np.isnan(shifted[-1])


# ---------------------------------------------------------------------------
# graphichelpers
# ---------------------------------------------------------------------------

class TestGraphicHelpers:

    def test_initialize_matplotlib_styles_no_error(self):
        """initialize_matplotlib_styles must not raise."""
        from pwml.utilities.graphichelpers import GraphicsStatics
        GraphicsStatics.initialize_matplotlib_styles(force=True)
        assert GraphicsStatics.g_styles_initialized is True

    def test_get_color_returns_tuple(self):
        from pwml.utilities.graphichelpers import GraphicsStatics
        GraphicsStatics.initialize_matplotlib_styles()
        color = GraphicsStatics.get_color(0)
        # seaborn colors are tuples of 3 or 4 floats
        assert len(color) in (3, 4)

    def test_get_color_wraps_around(self):
        from pwml.utilities.graphichelpers import GraphicsStatics
        GraphicsStatics.initialize_matplotlib_styles()
        # palette has 8 entries; index 8 should equal index 0
        assert GraphicsStatics.get_color(8) == GraphicsStatics.get_color(0)

    def test_get_hatch_wraps(self):
        from pwml.utilities.graphichelpers import GraphicsStatics
        n = len(GraphicsStatics.g_hatch)
        assert GraphicsStatics.get_hatch(n) == GraphicsStatics.get_hatch(0)

    def test_get_linestyle_wraps(self):
        from pwml.utilities.graphichelpers import GraphicsStatics
        n = len(GraphicsStatics.g_linestyle)
        assert GraphicsStatics.get_linestyle(n) == GraphicsStatics.get_linestyle(0)

    def test_threading_lock_exists(self):
        """The module-level _styles_lock must be a threading.Lock-like object."""
        import threading
        from pwml.utilities import graphichelpers as gph
        assert hasattr(gph, '_styles_lock')
        # Lock instances do not expose a public __class__ that is easy to check,
        # but they must have acquire/release methods.
        assert callable(gph._styles_lock.acquire)
        assert callable(gph._styles_lock.release)


# ---------------------------------------------------------------------------
# visualizationhelpers
# ---------------------------------------------------------------------------

class TestVisualizationHelpers:

    def _make_df(self):
        # plot_time_series expects Prophet-style DataFrames with a 'ds' date column.
        dates = pd.date_range('2024-01-01', periods=20, freq='D')
        return pd.DataFrame({'ds': dates, 'y': np.sin(np.arange(20) * 0.3)})

    def test_plot_time_series_no_error(self):
        """plot_time_series must not raise with minimal inputs."""
        from pwml.timeseries.visualizationhelpers import plot_time_series
        import matplotlib.pyplot as plt
        df = self._make_df()
        plot_time_series(
            title='Smoke Test',
            training=df,
            training_col='y',
            display=False)
        plt.close('all')

    def test_keyword_only_enforcement(self):
        """Passing optional params positionally after name= must raise TypeError."""
        from pwml.timeseries.visualizationhelpers import plot_time_series
        with pytest.raises(TypeError):
            # confidence is now keyword-only - positional call must fail
            plot_time_series(None, None, None, None)
