import zipfile
from pathlib import Path

import pytest

from backend.app.services.archive_extractor import extract_archive, is_archive


def test_is_archive():
    assert is_archive(Path("a.zip"))
    assert is_archive(Path("a.CBZ"))
    assert is_archive(Path("a.rar"))
    assert is_archive(Path("a.7z"))
    assert not is_archive(Path("a.jpg"))


def _zip(path, entries):
    with zipfile.ZipFile(path, "w") as zf:
        for name, data in entries.items():
            zf.writestr(name, data)


def test_zip_members_enumerated(tmp_path):
    z = tmp_path / "set.zip"
    _zip(z, {"a.jpg": b"img", "sub/b.png": b"img2", "notes.txt": b"hi"})
    with extract_archive(z) as members:
        names = sorted(n for n, _ in members)
        assert names == ["a.jpg", "notes.txt", "sub/b.png"]
        for _, p in members:
            assert p.is_file()
    for _, p in members:
        assert not p.exists()  # temp cleaned on exit


def test_cbz_alias(tmp_path):
    z = tmp_path / "set.cbz"
    _zip(z, {"p1.jpg": b"i"})
    with extract_archive(z) as members:
        assert [n for n, _ in members] == ["p1.jpg"]


def test_misnamed_zip_detected_and_extracted(tmp_path):
    """A real zip whose filename has no usable extension (Patreon attachment
    URL-blob name) is detected by magic bytes and its members extracted —
    previously it was filed as an opaque attachment and never opened."""
    z = tmp_path / "01_https___www.patreon.com_media-u_v3_131083093"
    _zip(z, {"a.jpg": b"img", "b.png": b"img2"})
    assert is_archive(z)
    with extract_archive(z) as members:
        assert sorted(n for n, _ in members) == ["a.jpg", "b.png"]


def test_non_archive_with_dotted_name_is_not_archive(tmp_path):
    """A non-archive file with a dotted/extension-less name isn't misdetected."""
    f = tmp_path / "01_https___www.patreon.com_media-u_v3_999"
    f.write_bytes(b"\x89PNG\r\n\x1a\n not really but not a zip")
    assert not is_archive(f)


def test_corrupt_archive_yields_nothing(tmp_path):
    bad = tmp_path / "broken.zip"
    bad.write_bytes(b"not a zip at all")
    with extract_archive(bad) as members:
        assert members == []


def test_7z_roundtrip(tmp_path):
    py7zr = pytest.importorskip("py7zr")
    a = tmp_path / "s.7z"
    (tmp_path / "x.jpg").write_bytes(b"img")
    with py7zr.SevenZipFile(a, "w") as zf:
        zf.write(tmp_path / "x.jpg", "x.jpg")
    with extract_archive(a) as members:
        assert [n for n, _ in members] == ["x.jpg"]


def test_rar_corrupt_is_failsoft(tmp_path):
    pytest.importorskip("rarfile")
    bad = tmp_path / "b.rar"
    bad.write_bytes(b"Rar! not really")
    with extract_archive(bad) as members:
        assert members == []  # invalid / no unrar → fail-soft, nothing lost
