#!/usr/bin/python3
"""Tests for slpkg.verify_files.VerifyFiles."""

from pathlib import Path
from unittest.mock import MagicMock

from slpkg.verify_files import VerifyFiles


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

PKG_FULL = "ffmpeg-6.1-x86_64-1_SBo"


def make_verify(
    log_packages: Path,
    packages: list[str],
    *,
    quiet: bool = False,
) -> VerifyFiles:
    """Construct VerifyFiles bypassing __init__ (avoids config_load)."""
    obj: VerifyFiles = object.__new__(VerifyFiles)
    obj.packages = packages
    obj.log_packages = log_packages
    obj.grey = ""
    obj.green = ""
    obj.yellow = ""
    obj.red = ""
    obj.endc = ""
    obj.option_quiet = quiet
    obj.virtual_prefixes = ("dev/", "proc/", "sys/", "run/")
    obj.skip_prefixes = ("install/",)
    obj.skip_suffixes = (".new", ".sample")
    obj.utils = MagicMock()
    obj.view_process = MagicMock()
    obj.missing = {}
    obj.checked_count = 0
    return obj


def write_pkg_log(log_packages: Path, full_name: str, content: str) -> None:
    log_packages.mkdir(parents=True, exist_ok=True)
    (log_packages / full_name).write_text(content, encoding="utf-8")


# ---------------------------------------------------------------------------
# _decode_octal_escapes
# ---------------------------------------------------------------------------


def test_decode_octal_escapes_plain_path() -> None:
    assert VerifyFiles._decode_octal_escapes("usr/bin/ffmpeg") == "usr/bin/ffmpeg"


def test_decode_octal_escapes_decodes_sequence() -> None:
    # \303\236 = 0xC3 0x9E = UTF-8 Þ
    result = VerifyFiles._decode_octal_escapes("usr/share/\\303\\236")
    assert "Þ" in result


def test_decode_octal_escapes_multiple_sequences() -> None:
    result = VerifyFiles._decode_octal_escapes("\\303\\236\\303\\236")
    assert result == "ÞÞ"


# ---------------------------------------------------------------------------
# _check_package_files — no FILE LIST section
# ---------------------------------------------------------------------------


def test_check_package_files_no_file_list_returns_empty(tmp_path: Path) -> None:
    write_pkg_log(tmp_path, PKG_FULL, "PACKAGE NAME: ffmpeg\nsome content\n")
    obj = make_verify(tmp_path, [])
    assert obj._check_package_files(tmp_path / PKG_FULL) == []


def test_check_package_files_oserror_returns_empty(tmp_path: Path) -> None:
    obj = make_verify(tmp_path, [])
    assert obj._check_package_files(tmp_path / "nonexistent") == []


# ---------------------------------------------------------------------------
# _check_package_files — skips
# ---------------------------------------------------------------------------


def test_check_package_files_skips_directories(tmp_path: Path) -> None:
    content = "FILE LIST:\n./usr/bin/\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert result == []


def test_check_package_files_skips_install_scripts(tmp_path: Path) -> None:
    content = "FILE LIST:\ninstall/slack-desc\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert result == []


def test_check_package_files_skips_new_suffix(tmp_path: Path) -> None:
    content = "FILE LIST:\netc/slpkg/slpkg.toml.new\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert result == []


def test_check_package_files_skips_sample_suffix(tmp_path: Path) -> None:
    content = "FILE LIST:\netc/app/config.sample\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert result == []


def test_check_package_files_skips_virtual_prefixes(tmp_path: Path) -> None:
    content = "FILE LIST:\ndev/null\nproc/1\nsys/kernel\nrun/lock\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert result == []


# ---------------------------------------------------------------------------
# _check_package_files — existing vs missing files
# ---------------------------------------------------------------------------


def test_check_package_files_existing_file_not_reported(tmp_path: Path) -> None:
    # Create a real file and reference it from the log
    real_file = tmp_path / "real_file.so"
    real_file.touch()
    content = f"FILE LIST:\n.{real_file}\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert str(real_file) not in result


def test_check_package_files_missing_file_reported(tmp_path: Path) -> None:
    # Reference a path whose parent exists but the file does not
    content = f"FILE LIST:\n.{tmp_path}/nonexistent_file.so\n"
    write_pkg_log(tmp_path, PKG_FULL, content)
    obj = make_verify(tmp_path, [])
    result = obj._check_package_files(tmp_path / PKG_FULL)
    assert any("nonexistent_file.so" in r for r in result)


# ---------------------------------------------------------------------------
# _display_results — quiet mode
# ---------------------------------------------------------------------------


def test_display_results_quiet_prints_pkg_names_only(capsys) -> None:
    obj = make_verify(Path("/"), [], quiet=True)
    obj.missing = {PKG_FULL: ["/usr/bin/ffmpeg"]}
    obj.checked_count = 1
    obj._display_results()
    out = capsys.readouterr().out
    assert PKG_FULL in out
    assert "/usr/bin/ffmpeg" not in out


def test_display_results_quiet_no_missing_prints_nothing(capsys) -> None:
    obj = make_verify(Path("/"), [], quiet=True)
    obj.missing = {}
    obj.checked_count = 1
    obj._display_results()
    out = capsys.readouterr().out
    assert out == ""


# ---------------------------------------------------------------------------
# _display_results — normal mode
# ---------------------------------------------------------------------------


def test_display_results_missing_prints_pkg_and_paths(capsys) -> None:
    obj = make_verify(Path("/"), [])
    obj.missing = {PKG_FULL: ["/usr/bin/ffmpeg", "/usr/lib64/libav.so"]}
    obj.checked_count = 1
    obj._display_results()
    out = capsys.readouterr().out
    assert PKG_FULL in out
    assert "/usr/bin/ffmpeg" in out
    assert "/usr/lib64/libav.so" in out
    assert "2 missing" in out


def test_display_results_no_missing_prints_passed(capsys) -> None:
    obj = make_verify(Path("/"), [])
    obj.missing = {}
    obj.checked_count = 3
    obj._display_results()
    out = capsys.readouterr().out
    assert "passed integrity check" in out
    assert "3" in out


def test_display_results_shows_summary_counts(capsys) -> None:
    obj = make_verify(Path("/"), [])
    obj.missing = {PKG_FULL: ["/usr/bin/ffmpeg"]}
    obj.checked_count = 5
    obj._display_results()
    out = capsys.readouterr().out
    assert "Checked: 5" in out
    assert "Issues: 1" in out
    assert "Missing files: 1" in out


# ---------------------------------------------------------------------------
# check() — return value
# ---------------------------------------------------------------------------


def test_check_returns_empty_list_when_no_missing(tmp_path: Path) -> None:
    obj = make_verify(tmp_path, [])
    obj.utils.all_installed.return_value = {}
    result = obj.check()
    assert result == []


def test_check_returns_short_names_of_broken_packages(tmp_path: Path) -> None:
    full_name = "ffmpeg-6.1-x86_64-1_SBo"
    write_pkg_log(tmp_path, full_name, f"FILE LIST:\n.{tmp_path}/missing_file.so\n")
    obj = make_verify(tmp_path, [])
    obj.utils.all_installed.return_value = {full_name: full_name}
    obj.utils.split_package.return_value = {"name": "ffmpeg", "tag": "_SBo"}
    result = obj.check()
    assert "ffmpeg" in result


def test_check_returns_only_broken_packages(tmp_path: Path) -> None:
    broken_full = "ffmpeg-6.1-x86_64-1_SBo"
    ok_full = "vlc-3.0-x86_64-1_SBo"
    real_file = tmp_path / "real_file.so"
    real_file.touch()
    write_pkg_log(tmp_path, broken_full, f"FILE LIST:\n.{tmp_path}/missing.so\n")
    write_pkg_log(tmp_path, ok_full, f"FILE LIST:\n.{real_file}\n")
    obj = make_verify(tmp_path, [])
    obj.utils.all_installed.return_value = {broken_full: broken_full, ok_full: ok_full}

    def split_side_effect(full_name: str) -> dict:
        return {"name": full_name.split("-")[0], "tag": "_SBo"}

    obj.utils.split_package.side_effect = split_side_effect
    result = obj.check()
    assert "ffmpeg" in result
    assert "vlc" not in result
