#!/usr/bin/python3
"""Tests for slpkg.checksum.Md5sum."""

import hashlib
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from slpkg.checksum import Md5sum


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


def make_md5sum(*, checksum_md5: bool = True) -> Md5sum:
    """Construct Md5sum bypassing __init__ (avoids config_load/View/Errors)."""
    obj: Md5sum = object.__new__(Md5sum)
    obj.checksum_md5 = checksum_md5
    obj.red = ""
    obj.endc = ""
    obj.errors = MagicMock()
    obj.view = MagicMock()
    return obj


# ---------------------------------------------------------------------------
# read_binary_file
# ---------------------------------------------------------------------------


def test_read_binary_file_returns_content(tmp_path: Path) -> None:
    f = tmp_path / "source.tar.gz"
    f.write_bytes(b"hello binary content")
    obj = make_md5sum()
    assert obj.read_binary_file(f) == b"hello binary content"


def test_read_binary_file_not_found_calls_errors(tmp_path: Path) -> None:
    obj = make_md5sum()
    obj.read_binary_file(tmp_path / "nonexistent.tar.gz")
    obj.errors.message.assert_called_once()
    assert "No such file or directory" in obj.errors.message.call_args[0][0]


def test_read_binary_file_oserror_calls_errors(tmp_path: Path) -> None:
    f = tmp_path / "file.tar.gz"
    f.write_bytes(b"data")
    obj = make_md5sum()
    with patch.object(Path, "read_bytes", side_effect=OSError("disk error")):
        obj.read_binary_file(f)
    obj.errors.message.assert_called_once()
    assert "Error reading file" in obj.errors.message.call_args[0][0]


# ---------------------------------------------------------------------------
# md5sum — checksum disabled
# ---------------------------------------------------------------------------


def test_md5sum_disabled_skips_check(tmp_path: Path) -> None:
    f = tmp_path / "source.tar.gz"
    f.write_bytes(b"any content")
    obj = make_md5sum(checksum_md5=False)
    # Should complete without reading the file or calling view.question
    obj.md5sum(tmp_path, "source.tar.gz", "deadbeef")
    obj.view.question.assert_not_called()


# ---------------------------------------------------------------------------
# md5sum — checksum enabled, match
# ---------------------------------------------------------------------------


def test_md5sum_correct_checksum_no_question(tmp_path: Path) -> None:
    content = b"slackware source content"
    f = tmp_path / "source.tar.gz"
    f.write_bytes(content)
    expected = hashlib.md5(content).hexdigest()

    obj = make_md5sum(checksum_md5=True)
    obj.md5sum(tmp_path, "source.tar.gz", expected)
    obj.view.question.assert_not_called()


# ---------------------------------------------------------------------------
# md5sum — checksum enabled, mismatch
# ---------------------------------------------------------------------------


def test_md5sum_wrong_checksum_calls_question(tmp_path: Path, capsys) -> None:
    content = b"slackware source content"
    f = tmp_path / "source.tar.gz"
    f.write_bytes(content)

    obj = make_md5sum(checksum_md5=True)
    obj.md5sum(tmp_path, "source.tar.gz", "wrongchecksum000000000000000000")
    obj.view.question.assert_called_once()


def test_md5sum_wrong_checksum_prints_expected_and_found(tmp_path: Path, capsys) -> None:
    content = b"data"
    f = tmp_path / "pkg.tar.gz"
    f.write_bytes(content)
    expected_md5 = hashlib.md5(content).hexdigest()

    obj = make_md5sum(checksum_md5=True)
    obj.md5sum(tmp_path, "pkg.tar.gz", "wrongchecksum000000000000000000")
    out = capsys.readouterr().out
    assert "Expected:" in out
    assert "Found:" in out
    assert expected_md5 in out


# ---------------------------------------------------------------------------
# md5sum — URL-encoded source name
# ---------------------------------------------------------------------------


def test_md5sum_url_encoded_source_name(tmp_path: Path) -> None:
    content = b"url encoded file"
    f = tmp_path / "my file.tar.gz"
    f.write_bytes(content)
    expected = hashlib.md5(content).hexdigest()

    obj = make_md5sum(checksum_md5=True)
    # URL-encoded space: %20
    obj.md5sum(tmp_path, "my%20file.tar.gz", expected)
    obj.view.question.assert_not_called()


# ---------------------------------------------------------------------------
# md5sum — source given as full URL path
# ---------------------------------------------------------------------------


def test_md5sum_source_as_url_extracts_filename(tmp_path: Path) -> None:
    content = b"source bytes"
    f = tmp_path / "archive.tar.xz"
    f.write_bytes(content)
    expected = hashlib.md5(content).hexdigest()

    obj = make_md5sum(checksum_md5=True)
    obj.md5sum(tmp_path, "https://example.com/files/archive.tar.xz", expected)
    obj.view.question.assert_not_called()
