from types import SimpleNamespace
from unittest.mock import MagicMock, Mock

import pytest

from glances.outputs.glances_curses import _GlancesCurses


@pytest.fixture
def glancescreen():
    """Create a lightweight _GlancesCurses instance for helper method testing.

    The full curses interface is intentionally bypassed in order to isolate
    and test layout/helper logic independently from terminal rendering.
    """
    # Bypass full curses initialization and create a lightweight instance
    screen = _GlancesCurses.__new__(_GlancesCurses)

    # Mock runtime arguments required by helper methods
    screen.args = SimpleNamespace(
        disable_cpu=False,
        disable_mem=False,
        disable_load=False,
        disable_quicklook=False,
        full_quicklook=False,
    )

    # Mock terminal dimensions
    screen.term_window = Mock()
    screen.term_window.getmaxyx.return_value = (24, 120)

    # Mock rendering-related methods to isolate layout logic
    screen.display_plugin = Mock()
    screen.new_column = Mock()

    # Initialize required internal state
    screen.space_between_column = 3
    screen._quicklook_max_width = 100
    screen._top = ['cpu', 'mem', 'load']

    return screen


class TestDisplayTopHelpers:
    """Tests for helper methods extracted from __display_top."""

    def test_get_stats_summary(self, glancescreen):
        """Ensure plugin width totals and active plugin counts are computed correctly."""
        stat_display = {
            'cpu': {'msgdict': ['cpu']},
            'mem': {'msgdict': ['mem']},
            'load': {'msgdict': []},
        }

        plugin_widths = {
            'cpu': 10,
            'mem': 20,
            'load': 5,
        }

        stats_width, stats_number = glancescreen._get_stats_summary(
            stat_display,
            plugin_widths,
        )

        assert stats_width == 35  # nosec B101
        assert stats_number == 2  # nosec B101

    def test_compute_spacing_single_plugin(self, glancescreen):
        """Ensure spacing logic behaves correctly when only one plugin is displayed."""
        stat_display = {
            'cpu': {'msgdict': ['cpu']},
        }

        plugin_widths = {
            'cpu': 10,
        }

        glancescreen._top = ['cpu']

        plugin_display_optional, _ = glancescreen._compute_spacing_and_optional(
            stat_display,
            plugin_widths,
            stats_width=10,
            stats_number=1,
        )

        assert glancescreen.space_between_column == 0  # nosec B101
        assert plugin_display_optional['cpu'] is True  # nosec B101

    def test_compute_spacing_disables_optional_stats(
        self,
        glancescreen,
    ):
        """Ensure optional CPU and MEM display elements are disabled when spacing is constrained."""
        stat_display = {
            'cpu': {'msgdict': ['cpu']},
            'mem': {'msgdict': ['mem']},
        }

        plugin_widths = {
            'cpu': 80,
            'mem': 80,
        }

        glancescreen._top = ['cpu', 'mem']

        # Simulate a constrained terminal width
        glancescreen.term_window.getmaxyx.return_value = (24, 40)

        # Mock reduced widths after optional content removal
        glancescreen.get_stats_display_width = MagicMock(return_value=20)

        plugin_display_optional, _ = glancescreen._compute_spacing_and_optional(
            stat_display,
            plugin_widths,
            stats_width=160,
            stats_number=2,
        )

        assert plugin_display_optional['cpu'] is False  # nosec B101
        assert plugin_display_optional['mem'] is False  # nosec B101
        assert glancescreen.space_between_column >= 1  # nosec B101

    def test_get_plugin_width(self, glancescreen):
        """Ensure plugin widths are correctly retrieved and mapped."""
        stat_display = {
            'cpu': {'msgdict': ['cpu']},
            'mem': {'msgdict': ['mem']},
        }

        glancescreen._top = ['cpu', 'mem']

        glancescreen.args.disable_cpu = False
        glancescreen.args.disable_mem = False

        glancescreen.get_stats_display_width = MagicMock(side_effect=[10, 20])

        plugin_widths = glancescreen._get_plugin_width(stat_display)

        assert plugin_widths == {
            'cpu': 10,
            'mem': 20,
        }  # nosec B101

    def test_handle_quicklook_plugin_unavailable(
        self,
        glancescreen,
    ):
        """Ensure unavailable quicklook plugins are handled gracefully."""
        stats = Mock()

        # Simulate unavailable quicklook plugin
        stats.get_plugin.side_effect = AttributeError

        stat_display = {
            'quicklook': {'msgdict': []},
        }

        plugin_widths = {}

        result_widths, result_stats_width = glancescreen._handle_quicklook_for_display(
            stat_display,
            stats,
            plugin_widths,
            stats_width=0,
            stats_number=1,
        )

        assert result_widths == plugin_widths  # nosec B101
        assert result_stats_width == 0  # nosec B101
