#!/usr/bin/python3
"""Tests for slpkg/stats.py using pytest."""

from unittest.mock import MagicMock, patch

import pytest
from slpkg.stats import Stats


@pytest.fixture
def mock_config():
    """Mock config_load for Stats."""
    with patch("slpkg.stats.config_load") as mock:
        mock.cyan = ""
        mock.green = ""
        mock.red = ""
        mock.endc = ""
        mock.bold = ""
        yield mock


@pytest.fixture
def stats_inst(mock_config):
    """Fixture for Stats instance with mocked dependencies."""
    with patch("slpkg.stats.Utilities"), \
         patch("slpkg.stats.ViewProcess"), \
         patch("shutil.get_terminal_size", return_value=(80, 24)):
        inst = Stats({"option_quiet": False})
        return inst


def test_get_stats(stats_inst):
    """Test gathering statistics from installed packages."""
    # Mock installed packages
    stats_inst.utils.all_installed.return_value = {
        "pkg1": "pkg1-1.0-x86_64-1",
        "pkg2": "pkg2-1.0-x86_64-1_SBo"
    }
    
    # Mock split_package to return tags
    def mock_split(name):
        if "_SBo" in name:
            return {"tag": "SBo"}
        return {"tag": ""}
    
    stats_inst.utils.split_package.side_effect = mock_split
    stats_inst.utils.package_log_size.return_value = 1024  # 1 KB per package
    
    data = stats_inst._get_stats()
    
    assert "slack" in data["repositories"]
    assert "sbo" in data["repositories"]
    assert data["repositories"]["slack"]["count"] == 1
    assert data["total_count"] == 2
    assert data["total_size"] == 2048


def test_run_stats_success(stats_inst, capsys):
    """Test successful stats display."""
    mock_data = {
        "repositories": {
            "slack": {"count": 10, "size": 100 * 1024},
            "sbo": {"count": 5, "size": 50 * 1024}
        },
        "packages": [],
        "total_count": 15,
        "total_size": 150 * 1024
    }
    stats_inst._get_stats = MagicMock(return_value=mock_data)
    stats_inst.utils.convert_file_sizes.side_effect = lambda x: f"{x/1024:.1f} KB"
    
    stats_inst.run()
    
    captured = capsys.readouterr()
    assert "System Statistics" in captured.out
    assert "slack" in captured.out
    assert "sbo" in captured.out
    assert "15 pkgs" in captured.out
    stats_inst.view_process.message.assert_called_once()
    stats_inst.view_process.done.assert_called_once()


def test_run_stats_empty(stats_inst, capsys):
    """Test stats display when no data is found."""
    stats_inst._get_stats = MagicMock(return_value={"repositories": {}})

    stats_inst.run()

    captured = capsys.readouterr()
    assert "No statistics available" in captured.out


_MOCK_DATA: dict = {
    "repositories": {
        "slack": {"count": 10, "size": 100 * 1024},
        "sbo": {"count": 5, "size": 50 * 1024},
    },
    "packages": [
        {"name": "pkg1", "full_name": "pkg1-1.0-x86_64-1", "size": 60 * 1024, "tag": "slack"},
        {"name": "pkg2", "full_name": "pkg2-1.0-x86_64-1_SBo", "size": 50 * 1024, "tag": "sbo"},
    ],
    "total_count": 15,
    "total_size": 150 * 1024,
}


def test_display_packages_per_repo_output(stats_inst, capsys) -> None:
    stats_inst.utils.convert_file_sizes.side_effect = lambda x: f"{x // 1024} KB"
    stats_inst._display_packages_per_repo(_MOCK_DATA["repositories"], _MOCK_DATA["total_count"])
    out = capsys.readouterr().out
    assert "Packages per repository" in out
    assert "slack" in out
    assert "sbo" in out
    assert "66.7%" in out   # slack: 10/15
    assert "33.3%" in out   # sbo:   5/15


def test_display_disk_usage_per_repo_output(stats_inst, capsys) -> None:
    stats_inst.utils.convert_file_sizes.side_effect = lambda x: f"{x // 1024} KB"
    stats_inst._display_disk_usage_per_repo(_MOCK_DATA["repositories"], _MOCK_DATA["total_size"])
    out = capsys.readouterr().out
    assert "Disk usage per repository" in out
    assert "slack" in out
    assert "sbo" in out
    assert "66.7%" in out   # slack: 100/150
    assert "33.3%" in out   # sbo:   50/150


def test_display_disk_usage_per_repo_sorted_by_size(stats_inst, capsys) -> None:
    """The repo with more disk usage must appear first."""
    stats_inst.utils.convert_file_sizes.side_effect = lambda x: f"{x} B"
    repos = {
        "small": {"count": 1, "size": 10},
        "large": {"count": 1, "size": 9000},
    }
    stats_inst._display_disk_usage_per_repo(repos, 9010)
    out = capsys.readouterr().out
    assert out.index("large") < out.index("small")


def test_display_disk_usage_zero_total(stats_inst, capsys) -> None:
    """total_size=0 must not raise ZeroDivisionError."""
    stats_inst.utils.convert_file_sizes.return_value = "0 B"
    stats_inst._display_disk_usage_per_repo({"slack": {"count": 0, "size": 0}}, 0)
    out = capsys.readouterr().out
    assert "0.0%" in out


def test_run_stats_shows_both_sections(stats_inst, capsys) -> None:
    stats_inst._get_stats = MagicMock(return_value=_MOCK_DATA)
    stats_inst.utils.convert_file_sizes.side_effect = lambda x: f"{x // 1024} KB"
    stats_inst.run()
    out = capsys.readouterr().out
    assert "Packages per repository" in out
    assert "Disk usage per repository" in out


def test_run_stats_json(stats_inst, capsys) -> None:
    import json as json_mod
    stats_inst.options["option_json"] = True
    stats_inst._get_stats = MagicMock(return_value=_MOCK_DATA)
    stats_inst.utils.convert_file_sizes.return_value = "150 KB"
    stats_inst.run()
    out = capsys.readouterr().out
    parsed = json_mod.loads(out)
    assert "summary" in parsed
    assert parsed["total_count"] == 15
