from pathlib import Path

import builtins
import json
import pytest

from ki2_externai import cli


@pytest.fixture
def fake_registry(monkeypatch):
    store = {}
    monkeypatch.setattr(cli, "load_registry", lambda: store)
    monkeypatch.setattr(cli, "save_registry", lambda data: store.update(data))
    monkeypatch.setattr(cli, "resolve_registered_path", lambda name, data=None: store.get(name))
    yield store


def _create_config(tmp_path: Path) -> Path:
    path = tmp_path / "externai.json"
    path.write_text(json.dumps({"version": 1, "imports": []}), encoding="utf-8")
    return path


def test_handle_register_creates_entry(monkeypatch, tmp_path, capsys, fake_registry):
    """Registering writes a new name→path pair and reports success."""
    monkeypatch.chdir(tmp_path)
    monkeypatch.setattr(builtins, "input", lambda _: "")

    cli.handle_register(None)

    captured = capsys.readouterr()
    assert "Saved" in captured.out
    assert tmp_path.name in fake_registry


def test_handle_unregister_by_name(monkeypatch, capsys, fake_registry):
    """Unregistering by name removes the entry."""
    fake_registry.update({"libA": "/tmp/libA"})
    cli.handle_unregister("libA")
    captured = capsys.readouterr()
    assert "Removed libA" in captured.out
    assert "libA" not in fake_registry


def test_handle_unregister_cwd(monkeypatch, tmp_path, capsys, fake_registry):
    """Unregistering without args removes any entries pointing to cwd."""
    monkeypatch.chdir(tmp_path)
    fake_registry.update({tmp_path.name: str(tmp_path)})

    cli.handle_unregister(None)

    captured = capsys.readouterr()
    assert "[registry] Removed" in captured.out
    assert tmp_path.name not in fake_registry


def test_handle_registry_list(monkeypatch, capsys, fake_registry):
    """List prints a table of all registered entries."""
    fake_registry.update({"libA": "/tmp/libA", "framework": "/tmp/fw"})

    cli.handle_registry_list()

    captured = capsys.readouterr()
    assert "name" in captured.out
    assert "/tmp/libA" in captured.out


def test_handle_add_from_registry(tmp_path, monkeypatch, capsys, fake_registry):
    """Adding by name pulls the registry path into externai.json."""
    monkeypatch.chdir(tmp_path)
    _create_config(tmp_path)
    fake_registry["libA"] = "/tmp/libA"

    cli.handle_add("libA", None)

    captured = capsys.readouterr()
    assert "added" in captured.out
    data = json.loads((tmp_path / "externai.json").read_text(encoding="utf-8"))
    assert "libA" in data["imports"]


def test_handle_add_with_path(tmp_path, capsys, monkeypatch):
    """Adding with a path stores an object entry."""
    monkeypatch.chdir(tmp_path)
    _create_config(tmp_path)
    target = tmp_path / "libA"
    target.mkdir()

    cli.handle_add("libA", str(target))

    data = json.loads((tmp_path / "externai.json").read_text(encoding="utf-8"))
    assert any(isinstance(entry, dict) and entry["name"] == "libA" for entry in data["imports"])


def test_handle_add_rejects_non_directory_path(tmp_path, capsys, monkeypatch):
    """Adding with a file path should fail with an explicit message."""
    monkeypatch.chdir(tmp_path)
    _create_config(tmp_path)
    target = tmp_path / "libA.txt"
    target.write_text("content", encoding="utf-8")

    cli.handle_add("libA", str(target))

    captured = capsys.readouterr()
    assert "is not a directory" in captured.out
    data = json.loads((tmp_path / "externai.json").read_text(encoding="utf-8"))
    assert data["imports"] == []


def test_handle_add_file_path_does_not_create_config(tmp_path, capsys, monkeypatch):
    """When config is missing, an invalid file path should not create a config file."""
    monkeypatch.chdir(tmp_path)
    target = tmp_path / "libA.txt"
    target.write_text("content", encoding="utf-8")

    cli.handle_add("libA", str(target))

    captured = capsys.readouterr()
    assert "is not a directory" in captured.out
    assert not (tmp_path / "externai.json").exists()


def test_handle_add_missing_registry(tmp_path, capsys, monkeypatch):
    """Adding a name that isn't registered prints a hint."""
    monkeypatch.chdir(tmp_path)
    _create_config(tmp_path)

    cli.handle_add("libA", None)

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


def test_handle_add_creates_config(tmp_path, capsys, monkeypatch):
    """Adding rebuilds externai.json when it is missing."""
    monkeypatch.chdir(tmp_path)
    target = tmp_path / "libA"
    target.mkdir()

    cli.handle_add("libA", str(target))

    data = json.loads((tmp_path / "externai.json").read_text(encoding="utf-8"))
    assert any(isinstance(entry, dict) and entry["name"] == "libA" for entry in data["imports"])


def test_handle_remove(tmp_path, capsys, monkeypatch):
    """Removing takes out the entry by name."""
    monkeypatch.chdir(tmp_path)
    config_path = _create_config(tmp_path)
    data = json.loads(config_path.read_text(encoding="utf-8"))
    data["imports"] = ["libA"]
    config_path.write_text(json.dumps(data), encoding="utf-8")

    cli.handle_remove("libA")

    captured = capsys.readouterr()
    assert "[remove] libA removed" in captured.out
    data = json.loads(config_path.read_text(encoding="utf-8"))
    assert "libA" not in data["imports"]


def test_prompt_yes_no_uses_default_on_eof(monkeypatch, capsys):
    """Non-interactive input should not crash and should fallback to default."""
    monkeypatch.setattr(
        builtins,
        "input",
        lambda _prompt: (_ for _ in ()).throw(EOFError()),
    )

    answer = cli.prompt_yes_no("Proceed?", default=False)

    captured = capsys.readouterr()
    assert answer is False
    assert "Input unavailable" in captured.err
