#!/usr/bin/python3
# -*- coding: utf-8 -*-

from unittest.mock import patch

from sun.pending import Pending, _pending_slackpkg, _pending_slpkg

# A realistic slackpkg PACKAGES.TXT block. Note there is NO 'PACKAGE VERSION:'
# field; the version is encoded in the 'PACKAGE NAME:' filename.
PACKAGES_TXT = """\
PACKAGES.TXT;  Fri Jan  1 00:00:00 UTC 2027

PACKAGE NAME:  vim-9.1-x86_64-1.txz
PACKAGE LOCATION:  ./slackware64/ap
PACKAGE SIZE (compressed):  9000 K
PACKAGE SIZE (uncompressed):  20000 K
PACKAGE DESCRIPTION:
vim: vim (Vi IMproved)
vim:

PACKAGE NAME:  curl-8.0.1-x86_64-1.txz
PACKAGE LOCATION:  ./slackware64/n
PACKAGE SIZE (compressed):  1000 K
PACKAGE SIZE (uncompressed):  3000 K
PACKAGE DESCRIPTION:
curl: curl (command line tool for transferring data)
curl:

PACKAGE NAME:  dejavu-fonts-ttf-2.37-noarch-3.txz
PACKAGE LOCATION:  ./slackware64/x
PACKAGE SIZE (compressed):  4000 K
PACKAGE SIZE (uncompressed):  8000 K
PACKAGE DESCRIPTION:
dejavu-fonts-ttf: dejavu-fonts-ttf (DejaVu fonts)
dejavu-fonts-ttf:
"""


class TestPendingSlackpkg:
    """Tests for the slackpkg PACKAGES.TXT parser."""

    def _write(self, tmp_path, content):
        path = tmp_path / 'PACKAGES.TXT'
        path.write_text(content, encoding='utf-8')
        return path

    def test_detects_pending_upgrade(self, tmp_path):
        path = self._write(tmp_path, PACKAGES_TXT)
        installed = {'vim': 'vim-9.0-x86_64-1', 'curl': 'curl-8.0.1-x86_64-1'}
        result = list(_pending_slackpkg(installed, path, set()))
        # vim is older (9.0 -> 9.1); curl matches; dejavu is not installed.
        assert result == ['vim-9.1 (installed: 9.0)']

    def test_up_to_date_yields_nothing(self, tmp_path):
        path = self._write(tmp_path, PACKAGES_TXT)
        installed = {'vim': 'vim-9.1-x86_64-1', 'curl': 'curl-8.0.1-x86_64-1'}
        assert list(_pending_slackpkg(installed, path, set())) == []

    def test_not_installed_is_skipped(self, tmp_path):
        path = self._write(tmp_path, PACKAGES_TXT)
        # Nothing installed -> repo packages are not pending for this system.
        assert list(_pending_slackpkg({}, path, set())) == []

    def test_hyphenated_package_name(self, tmp_path):
        path = self._write(tmp_path, PACKAGES_TXT)
        installed = {'dejavu-fonts-ttf': 'dejavu-fonts-ttf-2.36-noarch-3'}
        result = list(_pending_slackpkg(installed, path, set()))
        assert result == ['dejavu-fonts-ttf-2.37 (installed: 2.36)']

    def test_blacklisted_package_is_skipped(self, tmp_path):
        path = self._write(tmp_path, PACKAGES_TXT)
        installed = {'vim': 'vim-9.0-x86_64-1'}
        assert list(_pending_slackpkg(installed, path, {'vim'})) == []

    def test_non_utf8_bytes_do_not_crash(self, tmp_path):
        # Regression: an invalid byte in a description (e.g. latin-1 'ø' in
        # aspell-fo's slack-desc) must not break parsing of other packages.
        path = tmp_path / 'PACKAGES.TXT'
        raw = (
            b'PACKAGE NAME:  vim-9.1-x86_64-1.txz\n'
            b'PACKAGE SIZE (compressed):  9000 K\n'
            b'PACKAGE DESCRIPTION:\n'
            b'vim: GNU Aspell Faroese (F\xf8royskt) Dictionary\n'
        )
        path.write_bytes(raw)
        installed = {'vim': 'vim-9.0-x86_64-1'}
        result = list(_pending_slackpkg(installed, path, set()))
        assert result == ['vim-9.1 (installed: 9.0)']

    def test_missing_file_yields_nothing(self, tmp_path):
        path = tmp_path / 'does-not-exist' / 'PACKAGES.TXT'
        assert list(_pending_slackpkg({'vim': 'vim-9.0-x86_64-1'}, path, set())) == []


class TestPendingSlpkg:
    """Tests for the slpkg data.json parser."""

    DATA_JSON = (
        '{'
        '"vim": {"package": "vim-9.1-x86_64-1.txz"}, '
        '"curl": {"package": "curl-8.0.1-x86_64-1.txz"}'
        '}'
    )

    def _write(self, tmp_path, content):
        path = tmp_path / 'data.json'
        path.write_text(content, encoding='utf-8')
        return path

    def test_detects_pending_upgrade(self, tmp_path):
        path = self._write(tmp_path, self.DATA_JSON)
        installed = {'vim': 'vim-9.0-x86_64-1', 'curl': 'curl-8.0.1-x86_64-1'}
        result = list(_pending_slpkg(installed, path, set()))
        assert result == ['vim-9.1-x86_64-1.txz (installed: vim-9.0-x86_64-1)']

    def test_blacklisted_package_is_skipped(self, tmp_path):
        path = self._write(tmp_path, self.DATA_JSON)
        installed = {'vim': 'vim-9.0-x86_64-1'}
        assert list(_pending_slpkg(installed, path, {'vim'})) == []

    def test_invalid_json_yields_nothing(self, tmp_path):
        path = self._write(tmp_path, 'not json{')
        assert list(_pending_slpkg({'vim': 'vim-9.0-x86_64-1'}, path, set())) == []


class TestUpgradableDispatch:
    """Tests that Pending.upgradable() routes to the correct parser."""

    def test_routes_to_slackpkg(self, tmp_path):
        path = tmp_path / 'PACKAGES.TXT'
        path.write_text(PACKAGES_TXT, encoding='utf-8')
        with patch('sun.pending.Pending._detect_data_file', return_value=str(path)):
            with patch('sun.pending._installed', return_value={'vim': 'vim-9.0-x86_64-1'}):
                with patch('sun.pending.load_blacklist', return_value=set()):
                    assert Pending().upgradable() == ['vim-9.1 (installed: 9.0)']

    def test_routes_to_slpkg(self, tmp_path):
        path = tmp_path / 'data.json'
        path.write_text(TestPendingSlpkg.DATA_JSON, encoding='utf-8')
        with patch('sun.pending.Pending._detect_data_file', return_value=str(path)):
            with patch('sun.pending._installed', return_value={'vim': 'vim-9.0-x86_64-1'}):
                with patch('sun.pending.load_blacklist', return_value=set()):
                    assert Pending().upgradable() == ['vim-9.1-x86_64-1.txz (installed: vim-9.0-x86_64-1)']

    def test_no_data_file_returns_empty(self):
        with patch('sun.pending.Pending._detect_data_file', return_value=''):
            assert Pending().upgradable() == []
