"""Regression coverage for safe automatic profile-export destinations."""

from __future__ import annotations

import importlib
from argparse import Namespace
from pathlib import Path

import pytest
from hermes_cli import profile_cmd


@pytest.fixture()
def profiles():
    """Resolve the live profiles module at call time.

    Sibling test files reload ``hermes_cli.profiles`` / ``hermes_cli.main``;
    a top-level ``from hermes_cli.profiles import ...`` here would bind the
    pre-reload function objects and silently divorce this file's monkeypatches
    from the code under test when the whole directory runs as one sweep.
    """
    return importlib.import_module("hermes_cli.profiles")


def test_default_export_path_is_managed_and_outside_named_profiles(
    tmp_path, monkeypatch, profiles
):
    default_home = tmp_path / ".hermes"
    default_home.mkdir()
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)

    result = profiles.get_profile_export_path(
        "Research-Bot", timestamp="20260823-120000"
    )

    assert (
        result
        == default_home / "profile-exports" / "research-bot-20260823-120000.tar.gz"
    )
    assert result.parent.is_dir()
    assert not (default_home / "profiles" / "research-bot" / result.name).exists()


def test_custom_hermes_home_inside_a_checkout_uses_a_sibling_store(
    tmp_path, monkeypatch, profiles
):
    checkout = tmp_path / "checkout"
    checkout.mkdir()
    (checkout / ".git").write_text("gitdir: ../git\n", encoding="utf-8")
    monkeypatch.chdir(checkout)
    monkeypatch.setattr(Path, "home", lambda: tmp_path / "home")
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: checkout)

    result = profiles.get_profile_export_path("default", timestamp="20260823-120000")

    assert not result.resolve().is_relative_to(checkout.resolve())


def test_checkout_detection_does_not_depend_on_cwd(tmp_path, monkeypatch, profiles):
    """HERMES_HOME inside a checkout must be detected even when cwd is elsewhere.

    Regression for the cwd-anchored bypass: cron/service-manager invocations
    run from outside the checkout, and the original heuristic walked
    ``Path.cwd()`` — letting the export land inside the source tree again.
    """
    checkout = tmp_path / "checkout"
    checkout.mkdir()
    (checkout / ".git").write_text("gitdir: ../git\n", encoding="utf-8")
    hermes_home = checkout / "data_home"
    hermes_home.mkdir()
    elsewhere = tmp_path / "elsewhere"
    elsewhere.mkdir()
    monkeypatch.chdir(elsewhere)  # cwd has no .git ancestor inside tmp_path
    monkeypatch.setattr(Path, "home", lambda: tmp_path / "home")
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: hermes_home)

    result = profiles.get_profile_export_path("default", timestamp="20260823-120000")

    assert not result.resolve().is_relative_to(checkout.resolve())


def test_cli_export_rejects_bad_profile_name_without_traceback(
    tmp_path, monkeypatch, capsys, profiles
):
    """A bad name must print a clean error — the helper raises before export."""
    main_mod = importlib.import_module("hermes_cli.main")
    default_home = tmp_path / ".hermes"
    default_home.mkdir()
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)

    with pytest.raises(SystemExit):
        profile_cmd.cmd_profile(
            Namespace(
                profile_action="export",
                profile_name="bad//name",
                output=None,
            )
        )

    out = capsys.readouterr().out
    assert "Error:" in out


def test_cli_export_default_does_not_write_into_the_current_checkout(
    tmp_path, monkeypatch, capsys, profiles
):
    main_mod = importlib.import_module("hermes_cli.main")
    default_home = tmp_path / ".hermes"
    default_home.mkdir()
    (default_home / "config.yaml").write_text("model: test\n", encoding="utf-8")
    checkout = tmp_path / "checkout"
    checkout.mkdir()
    monkeypatch.chdir(checkout)
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)
    monkeypatch.setattr(
        "hermes_constants.get_default_hermes_root", lambda: default_home
    )

    profile_cmd.cmd_profile(
        Namespace(
            profile_action="export",
            profile_name="default",
            output=None,
        )
    )

    exported = list((default_home / "profile-exports").glob("default-*.tar.gz"))
    assert len(exported) == 1
    assert exported[0].parent == default_home / "profile-exports"
    assert not (checkout / "default.tar.gz").exists()
    assert str(exported[0]) in capsys.readouterr().out


def test_slash_export_uses_the_same_managed_destination(
    tmp_path, monkeypatch, profiles
):
    mixin_mod = importlib.import_module("hermes_cli.cli_commands_mixin")
    default_home = tmp_path / ".hermes"
    default_home.mkdir()
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)
    monkeypatch.setattr(profiles, "get_active_profile_name", lambda: "default")
    calls = []
    monkeypatch.setattr(
        profiles,
        "export_profile",
        lambda name, output: calls.append((name, output)) or output,
    )

    mixin_mod.CLICommandsMixin()._handle_export_command("/export")

    assert len(calls) == 1
    assert calls[0][0] == "default"
    assert Path(calls[0][1]).parent == default_home / "profile-exports"
    assert not (tmp_path / "default.tar.gz").exists()


@pytest.mark.asyncio
async def test_profile_export_api_uses_the_shared_managed_destination(
    tmp_path, monkeypatch, profiles
):
    from hermes_cli.web_models import ProfileExport

    router_mod = importlib.import_module("hermes_cli.web_routers.profiles")

    managed = tmp_path / "profile-exports" / "default-20260823-120000.tar.gz"
    monkeypatch.setattr(profiles, "get_profile_export_path", lambda name: managed)
    monkeypatch.setattr(
        profiles,
        "export_profile",
        lambda name, output, extra_files=None: output,
    )

    result = await router_mod.export_profile_endpoint("default", ProfileExport())

    assert result == {"ok": True, "archive": str(managed)}


def test_cwd_in_unrelated_checkout_does_not_prove_safety(
    tmp_path, monkeypatch, profiles
):
    """cwd inside unrelated checkout A must not stand in for the safety proof
    of a HERMES_HOME inside checkout B."""
    checkout_b = tmp_path / "checkout-b"
    checkout_b.mkdir()
    (checkout_b / ".git").mkdir()
    checkout_a = tmp_path / "checkout-a"
    checkout_a.mkdir()
    (checkout_a / ".git").write_text("gitdir: ../git\n", encoding="utf-8")
    monkeypatch.chdir(checkout_a)
    monkeypatch.setattr(Path, "home", lambda: tmp_path / "home")
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: checkout_b)

    result = profiles.get_profile_export_path("default", timestamp="20260823-120000")

    assert not result.resolve().is_relative_to(checkout_b.resolve())
    assert not result.resolve().is_relative_to(checkout_a.resolve())


def test_every_candidate_inside_a_checkout_fails_closed(
    tmp_path, monkeypatch, profiles
):
    """When home, sibling store, and tempdir all resolve inside checkouts the
    helper must refuse — a warning would not stop a scripted export from
    recreating the #92457 incident artifact."""
    import tempfile

    checkout = tmp_path / "checkout"
    checkout.mkdir()
    (checkout / ".git").mkdir()
    monkeypatch.setattr(Path, "home", lambda: checkout / "home")
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: checkout)
    monkeypatch.setattr(tempfile, "gettempdir", lambda: str(checkout / "tmp"))

    with pytest.raises(ValueError, match="No safe automatic export destination"):
        profiles.get_profile_export_path("default")


def test_export_dir_symlink_is_rejected(tmp_path, monkeypatch, profiles):
    """A pre-created symlink at the managed export path (predictable-path
    attack on shared hosts) must be refused, not silently followed."""
    default_home = tmp_path / ".hermes"
    default_home.mkdir()
    elsewhere = tmp_path / "elsewhere"
    elsewhere.mkdir()
    (default_home / "profile-exports").symlink_to(elsewhere)
    monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)

    with pytest.raises(ValueError, match="symlink"):
        profiles.get_profile_export_path("default")
