import pytest
from sunwaee.cli import app
from .conftest import ok, err


def test_create_note(runner, ws):
    data = ok(runner.invoke(app, ["note", "create", "--title", "My Note", "--body", "Hello", "--workspace", "personal"]))
    assert data["title"] == "My Note"
    assert data["body"] == "Hello"
    assert data["workspace"] == "personal"
    assert "path" in data


def test_create_note_with_tags(runner, ws):
    data = ok(runner.invoke(app, ["note", "create", "--title", "Tagged", "--tags", "a,b", "--workspace", "personal"]))
    assert data["tags"] == ["a", "b"]


def test_create_note_uses_default_workspace(runner, ws, monkeypatch):
    monkeypatch.setenv("SUNWAEE_WORKSPACE", "personal")
    data = ok(runner.invoke(app, ["note", "create", "--title", "Default ws note", "--body", "hi"]))
    assert data["workspace"] == "personal"


def test_create_note_creates_file(runner, ws, tmp_path):
    runner.invoke(app, ["note", "create", "--title", "File check", "--body", "x", "--workspace", "personal"])
    notes_dir = tmp_path / "workspaces" / "personal" / "notes"
    assert any(notes_dir.iterdir())


def test_list_notes_empty(runner, ws):
    data = ok(runner.invoke(app, ["note", "list", "--workspace", "personal"]))
    assert data == []


def test_list_notes(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Note A", "--body", "a", "--workspace", "personal"])
    runner.invoke(app, ["note", "create", "--title", "Note B", "--body", "b", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "list", "--workspace", "personal"]))
    assert len(data) == 2


def test_list_notes_filter_by_tag(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Tagged", "--tags", "work", "--workspace", "personal"])
    runner.invoke(app, ["note", "create", "--title", "Untagged", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "list", "--tags", "work", "--workspace", "personal"]))
    assert len(data) == 1
    assert data[0]["title"] == "Tagged"


def test_list_notes_includes_path(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Path check", "--body", "x", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "list", "--workspace", "personal"]))
    assert all("path" in n for n in data)


def test_show_note_by_title(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Show me", "--body", "content", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "show", "Show me", "--workspace", "personal"]))
    assert data["title"] == "Show me"
    assert data["body"] == "content"


def test_show_note_not_found(runner, ws):
    e = err(runner.invoke(app, ["note", "show", "ghost", "--workspace", "personal"]))
    assert e["code"] == "NOT_FOUND"


def test_show_note_includes_path(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Path note", "--body", "x", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "show", "Path note", "--workspace", "personal"]))
    assert "path" in data
    assert data["path"].endswith(".md")


def test_edit_note_title(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Old title", "--body", "x", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "edit", "Old title", "--title", "New title", "--workspace", "personal"]))
    assert data["title"] == "New title"


def test_edit_note_body(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Edit body", "--body", "original", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "edit", "Edit body", "--body", "updated", "--workspace", "personal"]))
    assert data["body"] == "updated"


def test_edit_note_tags(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Tag edit", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "edit", "Tag edit", "--tags", "x,y", "--workspace", "personal"]))
    assert data["tags"] == ["x", "y"]


def test_edit_note_updates_updated_at(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Timestamps", "--body", "v1", "--workspace", "personal"])
    d1 = ok(runner.invoke(app, ["note", "show", "Timestamps", "--workspace", "personal"]))
    ok(runner.invoke(app, ["note", "edit", "Timestamps", "--body", "v2", "--workspace", "personal"]))
    d2 = ok(runner.invoke(app, ["note", "show", "Timestamps", "--workspace", "personal"]))
    assert d2["updated_at"] >= d1["updated_at"]


def test_edit_note_not_found(runner, ws):
    e = err(runner.invoke(app, ["note", "edit", "ghost", "--title", "x", "--workspace", "personal"]))
    assert e["code"] == "NOT_FOUND"


def test_delete_note_requires_confirm(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "To delete", "--workspace", "personal"])
    e = err(runner.invoke(app, ["note", "delete", "To delete", "--workspace", "personal"]))
    assert e["code"] == "CONFIRMATION_REQUIRED"


def test_delete_note(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Bye", "--body", "x", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "delete", "Bye", "--confirm", "--workspace", "personal"]))
    assert data["title"] == "Bye"
    assert "path" in data
    e = err(runner.invoke(app, ["note", "show", "Bye", "--workspace", "personal"]))
    assert e["code"] == "NOT_FOUND"


def test_delete_note_not_found(runner, ws):
    e = err(runner.invoke(app, ["note", "delete", "ghost", "--confirm", "--workspace", "personal"]))
    assert e["code"] == "NOT_FOUND"


def test_search_notes_by_title(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Python tips", "--workspace", "personal"])
    runner.invoke(app, ["note", "create", "--title", "Rust notes", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "search", "python", "--workspace", "personal"]))
    assert len(data) == 1
    assert data[0]["title"] == "Python tips"


def test_search_notes_by_body(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "A", "--body", "async await syntax", "--workspace", "personal"])
    runner.invoke(app, ["note", "create", "--title", "B", "--body", "nothing relevant", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "search", "async", "--workspace", "personal"]))
    assert len(data) == 1


def test_search_notes_no_match(runner, ws):
    runner.invoke(app, ["note", "create", "--title", "Something", "--workspace", "personal"])
    data = ok(runner.invoke(app, ["note", "search", "zzznomatch", "--workspace", "personal"]))
    assert data == []


def test_note_id_is_stable(runner, ws):
    data = ok(runner.invoke(app, ["note", "create", "--title", "Stable id", "--body", "x", "--workspace", "personal"]))
    note_id = data["id"]
    data2 = ok(runner.invoke(app, ["note", "show", "Stable id", "--workspace", "personal"]))
    assert data2["id"] == note_id


# --- sort ---

def test_list_notes_sort_by_title(runner, ws):
    ok(runner.invoke(app, ["note", "create", "--title", "Zebra", "--workspace", "personal"]))
    ok(runner.invoke(app, ["note", "create", "--title", "Alpha", "--workspace", "personal"]))
    data = ok(runner.invoke(app, ["note", "list", "--sort", "title", "--workspace", "personal"]))
    titles = [n["title"] for n in data]
    assert titles == sorted(titles, key=str.lower)


def test_list_notes_sort_by_created(runner, ws):
    ok(runner.invoke(app, ["note", "create", "--title", "First", "--workspace", "personal"]))
    ok(runner.invoke(app, ["note", "create", "--title", "Second", "--workspace", "personal"]))
    data = ok(runner.invoke(app, ["note", "list", "--sort", "created", "--workspace", "personal"]))
    assert len(data) == 2


def test_list_notes_sort_invalid(runner, ws):
    e = err(runner.invoke(app, ["note", "list", "--sort", "banana", "--workspace", "personal"]))
    assert e["code"] == "VALIDATION_ERROR"
