"""Tests for scripts/setup_remediation.py — no network, no Java, no real downloads."""

import os
import stat
import sys

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))

import setup_remediation as sr


def _fake_java_script(path):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("#!/bin/sh\nexit 0\n")
    path.chmod(path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)


def test_check_missing_reports_absent_and_not_ok(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.delenv("ASUCB_REMEDIATION_OFFLINE", raising=False)
    monkeypatch.setattr(sr, "_odl_present", lambda: False)

    report = sr.check()

    assert report["ok"] is False
    assert report["jre"]["present"] is False
    assert report["verapdf"]["present"] is False
    assert report["odl"]["present"] is False
    assert report["offline"] is False


def test_check_exits_1_when_missing(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.setattr(sr, "_odl_present", lambda: False)
    monkeypatch.setattr(sys, "argv", ["setup_remediation.py", "--check"])

    with pytest.raises(SystemExit) as exc:
        sr.main()
    assert exc.value.code == 1


def test_check_present_with_fake_stamped_dirs(tmp_path, monkeypatch):
    vendor = tmp_path / "vendor"
    monkeypatch.setattr(sr, "VENDOR", vendor)
    _fake_java_script(vendor / "jre-21-aarch64" / "Contents" / "Home" / "bin" / "java")
    verapdf_dir = vendor / f"verapdf-{sr.VERAPDF_VERSION}"
    verapdf_dir.mkdir(parents=True)
    (verapdf_dir / "verapdf").write_text("#!/bin/sh\n")
    monkeypatch.setattr(sr, "_odl_present", lambda: True)

    report = sr.check()

    assert report["jre"]["present"] is True
    assert report["jre"]["healthy"] is True
    assert report["verapdf"]["present"] is True
    assert report["odl"]["present"] is True
    assert report["ok"] is True


def test_check_present_exits_0(tmp_path, monkeypatch):
    vendor = tmp_path / "vendor"
    monkeypatch.setattr(sr, "VENDOR", vendor)
    _fake_java_script(vendor / "jre-21-aarch64" / "Contents" / "Home" / "bin" / "java")
    verapdf_dir = vendor / f"verapdf-{sr.VERAPDF_VERSION}"
    verapdf_dir.mkdir(parents=True)
    (verapdf_dir / "verapdf").write_text("#!/bin/sh\n")
    monkeypatch.setattr(sr, "_odl_present", lambda: True)
    monkeypatch.setattr(sys, "argv", ["setup_remediation.py", "--check"])

    with pytest.raises(SystemExit) as exc:
        sr.main()
    assert exc.value.code == 0


def test_offline_env_var_honored_in_check(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.setenv("ASUCB_REMEDIATION_OFFLINE", "1")

    report = sr.check()

    assert report["offline"] is True


def test_offline_env_var_refuses_install_without_network(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.setenv("ASUCB_REMEDIATION_OFFLINE", "1")

    def _no_subprocess(*args, **kwargs):
        raise AssertionError("subprocess.run must not be called in offline mode")

    monkeypatch.setattr(sr.subprocess, "run", _no_subprocess)

    result = sr.install(auto_yes=True)

    assert result == 1


def test_install_declined_prompt_skips_downloads(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.delenv("ASUCB_REMEDIATION_OFFLINE", raising=False)

    def _no_subprocess(*args, **kwargs):
        raise AssertionError("subprocess.run (curl) must not be called without confirmation")

    monkeypatch.setattr(sr.subprocess, "run", _no_subprocess)
    monkeypatch.setattr("builtins.input", lambda *_: "n")

    result = sr.install(auto_yes=False)

    assert result == 1


def test_get_java_and_get_verapdf_none_when_absent(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    assert sr.get_java() is None
    assert sr.get_verapdf() is None


def test_get_libreoffice_none_when_absent(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    assert sr.get_libreoffice() is None


def _fake_soffice(vendor):
    path = vendor / f"libreoffice-{sr.LIBREOFFICE_VERSION}" / "LibreOffice.app" / "Contents" / "MacOS" / "soffice"
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("#!/bin/sh\nexit 0\n")
    return path


def test_get_libreoffice_present_at_pinned_layout(tmp_path, monkeypatch):
    vendor = tmp_path / "vendor"
    monkeypatch.setattr(sr, "VENDOR", vendor)
    expected = _fake_soffice(vendor)
    assert sr.get_libreoffice() == expected


def test_check_reports_libreoffice_but_does_not_gate_ok(tmp_path, monkeypatch):
    vendor = tmp_path / "vendor"
    monkeypatch.setattr(sr, "VENDOR", vendor)
    monkeypatch.setattr(sr, "_odl_present", lambda: False)
    # no soffice installed -- libreoffice absent, but overall `ok` (jre/verapdf/odl
    # only) is independently False here anyway; assert the libreoffice key itself
    # is report-only and never referenced by the `ok` computation.
    report = sr.check()
    assert report["libreoffice"]["present"] is False
    assert "libreoffice" not in {"jre", "verapdf", "odl"}  # sanity: not part of ok's inputs

    _fake_soffice(vendor)
    report2 = sr.check()
    assert report2["libreoffice"]["present"] is True
    assert report2["ok"] == report["ok"]  # unchanged by libreoffice presence either way


def test_libreoffice_url_matches_pinned_version_and_arch():
    url = sr.libreoffice_url()
    assert sr.LIBREOFFICE_VERSION in url
    assert url.startswith("https://download.documentfoundation.org/libreoffice/stable/")
    assert url.endswith(".dmg")


def test_offline_env_var_refuses_install_libreoffice(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.setenv("ASUCB_REMEDIATION_OFFLINE", "1")

    def _no_subprocess(*args, **kwargs):
        raise AssertionError("subprocess.run must not be called in offline mode")

    monkeypatch.setattr(sr.subprocess, "run", _no_subprocess)

    assert sr.install_libreoffice(auto_yes=True) == 1


def test_install_libreoffice_declined_prompt_skips_download(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.delenv("ASUCB_REMEDIATION_OFFLINE", raising=False)

    def _no_subprocess(*args, **kwargs):
        raise AssertionError("subprocess.run (curl) must not be called without confirmation")

    monkeypatch.setattr(sr.subprocess, "run", _no_subprocess)
    monkeypatch.setattr("builtins.input", lambda *_: "n")

    assert sr.install_libreoffice(auto_yes=False) == 1


def test_install_libreoffice_already_present_skips_download(tmp_path, monkeypatch):
    vendor = tmp_path / "vendor"
    monkeypatch.setattr(sr, "VENDOR", vendor)
    _fake_soffice(vendor)

    def _no_subprocess(*args, **kwargs):
        raise AssertionError("subprocess.run must not be called when already installed")

    monkeypatch.setattr(sr.subprocess, "run", _no_subprocess)

    assert sr.install_libreoffice(auto_yes=True) == 0


def test_cli_install_libreoffice_flag_routes_correctly(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    calls = []
    monkeypatch.setattr(sr, "install_libreoffice", lambda auto_yes: calls.append(auto_yes) or 0)
    monkeypatch.setattr(sys, "argv", ["setup_remediation.py", "--install-libreoffice", "--yes"])

    with pytest.raises(SystemExit) as exc:
        sr.main()
    assert exc.value.code == 0
    assert calls == [True]


def test_install_phase2_deps_pins_to_installed_versions(monkeypatch):
    calls = []
    monkeypatch.setattr(sr.subprocess, "run", lambda cmd, **kw: calls.append(cmd))
    monkeypatch.setattr(sr.importlib.metadata, "version",
                         lambda name: {"pymupdf": "1.28.0"}.get(name) or
                         (_ for _ in ()).throw(sr.importlib.metadata.PackageNotFoundError(name)))

    sr._install_phase2_deps()

    assert len(calls) == 1
    specs = calls[0][calls[0].index("install") + 1:]
    assert "pymupdf==1.28.0" in specs
    # ponytail: unpinned when no installed version is found (see module docstring)
    assert "pikepdf" in specs
    assert not any(s.startswith("pikepdf==") for s in specs)


def test_install_phase2_deps_all_unpinned_when_nothing_installed(monkeypatch):
    calls = []
    monkeypatch.setattr(sr.subprocess, "run", lambda cmd, **kw: calls.append(cmd))

    def _raise(name):
        raise sr.importlib.metadata.PackageNotFoundError(name)

    monkeypatch.setattr(sr.importlib.metadata, "version", _raise)
    sr._install_phase2_deps()

    specs = calls[0][calls[0].index("install") + 1:]
    assert set(specs) == set(sr.PHASE2_PIP_DEPS)


def test_install_calls_phase2_deps_after_confirmation(tmp_path, monkeypatch):
    monkeypatch.setattr(sr, "VENDOR", tmp_path / "vendor")
    monkeypatch.delenv("ASUCB_REMEDIATION_OFFLINE", raising=False)
    monkeypatch.setattr(sr, "_install_jre", lambda tmp_dir: None)
    monkeypatch.setattr(sr, "_install_verapdf", lambda tmp_dir: None)
    monkeypatch.setattr(sr, "_install_odl", lambda: None)
    calls = []
    monkeypatch.setattr(sr, "_install_phase2_deps", lambda: calls.append(True))

    result = sr.install(auto_yes=True)

    assert result == 0
    assert calls == [True]
