#!/usr/bin/python3
"""Tests for slpkg.download_only.DownloadOnly."""

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

import pytest

from slpkg.download_only import DownloadOnly


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

SBO_DATA = {
    "ffmpeg": {
        "location": "multimedia/ffmpeg",
        "download": ["https://ffmpeg.org/ffmpeg-6.1.tar.xz"],
        "download64": ["https://ffmpeg.org/ffmpeg-6.1-64.tar.xz"],
    }
}

BIN_DATA = {
    "ffmpeg": {
        "package": "ffmpeg-6.1-x86_64-1_SBo.txz",
        "mirror": "https://mirrors.slackware.com/",
        "location": "slackware64/l",
    }
}


def make_download_only(
    tmp_path: Path,
    data: dict,
    *,
    repository: str = "sbo",
    gpg: bool = False,
    is_64bit: bool = True,
) -> DownloadOnly:
    """Construct DownloadOnly bypassing __init__ (avoids config_load/Repositories)."""
    obj: DownloadOnly = object.__new__(DownloadOnly)
    obj.directory = tmp_path
    obj.options = {}
    obj.data = data
    obj.repository = repository
    obj.gpg_verification = gpg
    obj.is_64bit = is_64bit
    obj.urls = {}
    obj.asc_files = []
    obj.count_sources = 0

    repos = MagicMock()
    repos.sbo_repo_name = "sbo"
    repos.ponce_repo_name = "ponce"
    repos.sbo_repo_tar_suffix = ".tar.gz"
    repos.repositories = {
        "sbo": {
            "path": tmp_path / "sbo_repo",
            "mirror_packages": "https://slackbuilds.org/slackbuilds/15.0/",
        }
    }
    obj.repos = repos

    obj.view = MagicMock()
    obj.download = MagicMock()
    obj.utils = MagicMock()
    obj.utils.apply_package_pattern.side_effect = lambda data, pkgs: pkgs
    obj.imp = MagicMock()
    obj.imp.done = "Done."
    obj.errors = MagicMock()
    obj.gpg = MagicMock()
    return obj


# ---------------------------------------------------------------------------
# save_binary_sources
# ---------------------------------------------------------------------------


def test_save_binary_sources_adds_url(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack")
    obj.save_binary_sources("ffmpeg")
    assert "ffmpeg" in obj.urls
    urls, dest = obj.urls["ffmpeg"]
    assert "ffmpeg-6.1-x86_64-1_SBo.txz" in urls[0]
    assert dest == tmp_path


def test_save_binary_sources_increments_count(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack")
    obj.save_binary_sources("ffmpeg")
    assert obj.count_sources == 1


def test_save_binary_sources_gpg_adds_asc_url(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack", gpg=True)
    obj.save_binary_sources("ffmpeg")
    assert "ffmpeg_asc" in obj.urls
    assert len(obj.asc_files) == 1
    assert obj.asc_files[0].name.endswith(".asc")


def test_save_binary_sources_no_gpg_no_asc(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack", gpg=False)
    obj.save_binary_sources("ffmpeg")
    assert "ffmpeg_asc" not in obj.urls
    assert obj.asc_files == []


# ---------------------------------------------------------------------------
# save_slackbuild_sources
# ---------------------------------------------------------------------------


def test_save_slackbuild_sources_64bit_uses_download64(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo", is_64bit=True)
    obj.save_slackbuild_sources("ffmpeg")
    urls, dest = obj.urls["ffmpeg"]
    assert "ffmpeg-6.1-64.tar.xz" in urls[0]


def test_save_slackbuild_sources_32bit_uses_download(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo", is_64bit=False)
    obj.save_slackbuild_sources("ffmpeg")
    urls, dest = obj.urls["ffmpeg"]
    assert "ffmpeg-6.1.tar.xz" in urls[0]


def test_save_slackbuild_sources_no_download64_falls_back(tmp_path: Path) -> None:
    data = {
        "ffmpeg": {
            "location": "multimedia/ffmpeg",
            "download": ["https://ffmpeg.org/ffmpeg-6.1.tar.xz"],
        }
    }
    obj = make_download_only(tmp_path, data, repository="sbo", is_64bit=True)
    obj.save_slackbuild_sources("ffmpeg")
    urls, _ = obj.urls["ffmpeg"]
    assert "ffmpeg-6.1.tar.xz" in urls[0]


def test_save_slackbuild_sources_dest_is_subdir(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo")
    obj.save_slackbuild_sources("ffmpeg")
    _, dest = obj.urls["ffmpeg"]
    assert dest == tmp_path / "ffmpeg"


def test_save_slackbuild_sources_gpg_sbo_adds_asc(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo", gpg=True)
    obj.save_slackbuild_sources("ffmpeg")
    assert "ffmpeg_sbo_asc" in obj.urls
    assert len(obj.asc_files) == 1


def test_save_slackbuild_sources_gpg_ponce_no_asc(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="ponce", gpg=True)
    obj.save_slackbuild_sources("ffmpeg")
    assert "ffmpeg_sbo_asc" not in obj.urls
    assert obj.asc_files == []


def test_save_slackbuild_sources_increments_count(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo", is_64bit=True)
    obj.save_slackbuild_sources("ffmpeg")
    assert obj.count_sources == 1


# ---------------------------------------------------------------------------
# copy_slackbuild_scripts
# ---------------------------------------------------------------------------


def test_copy_slackbuild_scripts_copies_tree(tmp_path: Path) -> None:
    src = tmp_path / "sbo_repo" / "multimedia" / "ffmpeg" / "ffmpeg"
    src.mkdir(parents=True)
    (src / "ffmpeg.SlackBuild").write_text("#!/bin/bash\n", encoding="utf-8")

    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo")
    obj.repos.repositories["sbo"]["path"] = tmp_path / "sbo_repo"
    obj.copy_slackbuild_scripts("ffmpeg")

    dest = tmp_path / "ffmpeg"
    assert dest.is_dir()
    assert (dest / "ffmpeg.SlackBuild").exists()


def test_copy_slackbuild_scripts_skips_if_target_exists(tmp_path: Path) -> None:
    src = tmp_path / "sbo_repo" / "multimedia" / "ffmpeg" / "ffmpeg"
    src.mkdir(parents=True)
    dest = tmp_path / "ffmpeg"
    dest.mkdir()
    (dest / "existing.txt").write_text("existing", encoding="utf-8")

    obj = make_download_only(tmp_path, SBO_DATA, repository="sbo")
    obj.repos.repositories["sbo"]["path"] = tmp_path / "sbo_repo"
    obj.copy_slackbuild_scripts("ffmpeg")

    # existing file must still be there (no overwrite)
    assert (dest / "existing.txt").exists()


# ---------------------------------------------------------------------------
# download_the_sources
# ---------------------------------------------------------------------------


def test_download_the_sources_calls_download_when_urls(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack")
    obj.urls = {"ffmpeg": (["https://example.com/ffmpeg.txz"], tmp_path)}
    obj.data["ffmpeg"]["location"] = "l"
    obj.download_the_sources()
    obj.download.download.assert_called_once()


def test_download_the_sources_skips_when_no_urls(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack")
    obj.urls = {}
    obj.download_the_sources()
    obj.download.download.assert_not_called()


# ---------------------------------------------------------------------------
# gpg_verify
# ---------------------------------------------------------------------------


def test_gpg_verify_calls_verify_when_enabled_and_asc_files(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack", gpg=True)
    obj.asc_files = [tmp_path / "ffmpeg.txz.asc"]
    obj.gpg_verify()
    obj.gpg.verify.assert_called_once_with(obj.asc_files)


def test_gpg_verify_skips_when_gpg_disabled(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack", gpg=False)
    obj.asc_files = [tmp_path / "ffmpeg.txz.asc"]
    obj.gpg_verify()
    obj.gpg.verify.assert_not_called()


def test_gpg_verify_skips_for_ponce_repo(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, SBO_DATA, repository="ponce", gpg=True)
    obj.asc_files = [tmp_path / "ffmpeg.tar.gz.asc"]
    obj.gpg_verify()
    obj.gpg.verify.assert_not_called()


def test_gpg_verify_skips_when_no_asc_files(tmp_path: Path) -> None:
    obj = make_download_only(tmp_path, BIN_DATA, repository="slack", gpg=True)
    obj.asc_files = []
    obj.gpg_verify()
    obj.gpg.verify.assert_not_called()
