from pathlib import Path

import pytest

from ki2_externai import operations
from ki2_externai.config import ExternalImport, ExternalsConfig


class _FakeProcess:
    def __init__(self, returncode=0, stdout="", stderr=""):
        self.returncode = returncode
        self.stdout = stdout
        self.stderr = stderr


def _make_config(tmp_path: Path) -> ExternalsConfig:
    externals_dir = tmp_path / "out"
    source = tmp_path / "libA"
    source.mkdir()
    entry = ExternalImport(name="libA", path=str(source), mount="libA")
    return ExternalsConfig(version=1, externals_dir=externals_dir, imports=[entry])


def _config_without_path(tmp_path: Path) -> ExternalsConfig:
    externals_dir = tmp_path / "out"
    entry = ExternalImport(name="libA", path=None, mount="libA")
    return ExternalsConfig(version=1, externals_dir=externals_dir, imports=[entry])


@pytest.fixture(autouse=True)
def _patch_os(monkeypatch):
    """Prevent filesystem/mount helpers from touching the real host."""
    monkeypatch.setattr(operations, "ensure_dir", lambda path: None)
    monkeypatch.setattr(operations, "resolve_source", lambda path: Path(path))


@pytest.fixture
def command_log(monkeypatch):
    """Capture every command submitted to `operations.run`."""
    calls = []

    def _fake_run(cmd, check=True):
        calls.append(cmd)
        return _FakeProcess()

    monkeypatch.setattr(operations, "run", _fake_run)
    return calls


def test_do_mount_runs_bind(command_log, monkeypatch, tmp_path):
    """Mount issues should run a bind followed by a remount."""
    config = _make_config(tmp_path)
    monkeypatch.setattr(operations, "load_registry", lambda: {})
    monkeypatch.setattr(operations, "resolve_registered_path", lambda *_, **__: None)
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: False)

    operations.do_mount(config)

    assert "--bind" in command_log[0]
    assert any(any("remount" in part for part in call) for call in command_log)


def test_do_unmount_logs_missing(monkeypatch, tmp_path, capsys):
    """Unmount prints a notice when nothing is mounted."""
    config = _make_config(tmp_path)
    monkeypatch.setattr(operations, "run", lambda *_, **__: _FakeProcess())
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: False)

    monkeypatch.setattr(operations, "load_registry", lambda: {})
    operations.do_unmount(config)

    captured = capsys.readouterr()
    assert "not mounted" in captured.out


def test_do_unmount_clears_target(monkeypatch, tmp_path):
    """After a successful umount we remove the mount directory to avoid ghost statuses."""
    config = _make_config(tmp_path)
    target = config.externals_dir / "libA"
    target.mkdir(parents=True, exist_ok=True)
    monkeypatch.setattr(operations, "run", lambda *_, **__: _FakeProcess())
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: True)

    operations.do_unmount(config)

    assert not target.exists()


def test_do_unmount_keeps_non_empty_target(monkeypatch, tmp_path, capsys):
    """Unmount should never recursively delete a non-empty mount target."""
    config = _make_config(tmp_path)
    target = config.externals_dir / "libA"
    target.mkdir(parents=True, exist_ok=True)
    marker = target / "keep.txt"
    marker.write_text("do-not-delete", encoding="utf-8")
    monkeypatch.setattr(operations, "run", lambda *_, **__: _FakeProcess())
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: True)

    operations.do_unmount(config)

    captured = capsys.readouterr()
    assert target.exists()
    assert marker.exists()
    assert "not empty" in captured.err


def test_do_status_reports_unregistered(monkeypatch, tmp_path, capsys):
    """Status output should flag imports without registered paths."""
    config = _config_without_path(tmp_path)
    monkeypatch.setattr(operations, "load_registry", lambda: {})
    monkeypatch.setattr(operations, "resolve_registered_path", lambda *_, **__: None)
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: False)
    monkeypatch.setattr(operations, "resolve_registered_path", lambda *_, **__: None)
    monkeypatch.setattr(operations, "load_registry", lambda: {})

    operations.do_status(config)

    captured = capsys.readouterr()
    assert "<unregistered>" in captured.out


def test_do_doctor_reports_ntfs(monkeypatch, tmp_path, capsys):
    """Doctor warns about NTFS sources and still reports a summary."""
    config = _make_config(tmp_path)
    monkeypatch.setattr(operations, "load_registry", lambda: {})
    monkeypatch.setattr(operations, "resolve_registered_path", lambda *_, **__: "/tmp/libA")
    monkeypatch.setattr(operations, "on_ntfs", lambda path: True)
    monkeypatch.setattr(operations, "have", lambda tool: True)
    monkeypatch.setattr(operations, "is_mounted", lambda tgt: False)
    monkeypatch.setattr(operations, "run", lambda *_, **__: _FakeProcess())

    operations.do_doctor(config)

    captured = capsys.readouterr()
    assert "WARNING" in captured.out
    assert "Checks complete." in captured.out
