import pytest

from controllers.types import Messages
from features.message_buttons.shaping import MOST, shaped
from resources.base import AGENT
from tests.conftest import fresh


def test_a_button_names_a_type_and_action_and_what_would_not_run_is_dropped():
    record = fresh()
    rows = Messages(record, actor=AGENT)

    start = {"label": "Okay, approve", "type": "plan", "n": 3, "action": "approve"}
    assert shaped(record, [start]) == [start], "a button on a row is kept as it was written"
    assert shaped(record, [{"label": "New to-do", "type": "todo", "action": "create", "body": {"title": "one"}}]) == \
        [{"label": "New to-do", "type": "todo", "action": "create", "body": {"title": "one"}}], "a type-level button needs no row"
    assert shaped(record, [{"label": "Done", "type": "todo", "n": 1, "action": "done"}]) == \
        [{"label": "Done", "type": "todo", "action": "done", "n": 1}], "a word the type renames is an action too"

    assert shaped(record, [{"label": "Go", "type": "nonsense", "n": 1, "action": "all"}]) == [], "a type nobody has is dropped"
    assert shaped(record, [{"label": "Go", "type": "plan", "n": 1, "action": "detonate"}]) == [], \
        "an action the type does not have is dropped"
    assert shaped(record, [{"label": "  ", "type": "plan", "n": 1, "action": "approve"}]) == [], "a button with no label is dropped"
    assert (shaped(record, "go"), shaped(record, [7])) == ([], []), "what is not a list of objects is nothing"
    assert len(shaped(record, [start] * (MOST + 3))) == MOST, "no more than five are kept"
    long = "Apply the suggestions, and give the viewer its own feature text"
    assert shaped(record, [{**start, "label": long}])[0]["label"] == long, "a button's whole label is kept, not cut at forty characters"
    assert shaped(record, [{**start, "again": True}]) == [{**start, "again": True}], "a button may say it can be pressed again"
    options = [{"label": "Accept option A", "say": "A", "choice": "option"}, {"label": "Accept option B", "say": "B", "choice": "option"}]
    assert [b.get("choice") for b in shaped(record, options)] == ["option", "option"], "buttons that are one decision keep their shared choice"
    asked = [{**options[0], "ask": " Which one? "}, options[1]]
    assert shaped(record, asked)[0]["ask"] == "Which one?", "the first button of a choice may carry the question it answers"

    made = rows.create("Ready when you are", buttons=[start, {"label": "Go", "type": "plan", "n": 1, "action": "detonate"}])
    assert rows.load(made.n).data["buttons"] == [start], "the message keeps the button that runs and drops the one that does not"
    plain = rows.create("Nothing to press")
    assert ("buttons" in rows.load(plain.n).data) is False, "a message with no buttons is left alone"
    later = rows.create("buttons put on afterwards")
    rows.update(later.n, buttons=[{"label": "Go", "type": "plan", "n": 1, "action": "detonate"}, start])
    assert rows.load(later.n).data["buttons"] == [start], "buttons added after the message was written are cleaned the same way"
    from controllers.types import Todos
    from features.message_buttons.pressing import press
    from resources.base import USER, Stale
    new = {"label": "New to-do", "type": "todo", "action": "create", "body": {"title": "from a button"}}
    offered = rows.create("Want a to-do?", buttons=[new])
    press(record, rows.load(offered.n), "New to-do", USER, "viewer")
    assert [row.title for row in Todos(record, actor=AGENT).rows.standing()] == ["from a button"], "a button that names no row runs its action on the type"
    assert rows.load(offered.n).data["pressed"] == ["New to-do"], "and the message remembers it was pressed"
    with pytest.raises(Stale):
        press(record, rows.load(offered.n), "New to-do", USER, "viewer")
    target = Todos(record, actor=AGENT).create("close me from a button")
    closing = rows.create("Close it?", buttons=[{"label": "Close it", "type": "todo", "n": target.n, "action": "complete", "body": {"how": "pressed"}}])
    press(record, rows.load(closing.n), "Close it", USER, "viewer")
    assert Todos(record, actor=AGENT).load(target.n).completed, "a button that names a row runs its action on that row"


def test_a_document_carries_buttons_that_say_something_for_the_user():
    import features
    from controllers.types import Docs
    features.load()
    record = fresh()
    buttons = [{"label": "Accept this proposal", "say": "I accept this proposal"}, {"label": "Go", "type": "plan", "n": 1, "action": "detonate"}]
    doc = Docs(record, actor=AGENT).create("A proposal", buttons=buttons)
    assert Docs(record, actor=AGENT).load(doc.n).data["buttons"] == [{"label": "Accept this proposal", "say": "I accept this proposal"}], \
        "a say button is kept on a document, and one that would not run is dropped"
    from controllers.types import Reports
    from features.message_buttons.pressing import unspent
    choice = [{"label": "Reshape it", "say": "Reshape it", "choice": "answer"}, {"label": "Keep it", "say": "Keep it", "choice": "answer"}]
    report = Reports(record, actor=AGENT).create("A choice", buttons=choice)
    assert len(unspent(Reports(record, actor=AGENT).load(report.n))) == 2, "an open choice offers both of its buttons"
    Reports(record, actor=AGENT).action("set")(report.n, key="answered_own", value="Yep, and in Docker")
    assert unspent(Reports(record, actor=AGENT).load(report.n)) == [], "an answer in your own words settles the choice like a pressed button"
    from controllers.types import Comments
    from resources.base import USER
    waiting = Reports(record, actor=AGENT).create("A third choice", buttons=choice)
    Comments(record, actor=USER).create("Keep it, and tidy it up", brief="Keep it, and tidy it up", about=waiting.ref)
    assert (unspent(Reports(record, actor=AGENT).load(waiting.n)), Reports(record, actor=AGENT).load(waiting.n).data.get("answered_own")) == ([], "Keep it, and tidy it up"), \
        "a comment you write on a row that waits on its buttons answers it in your own words, so it stops asking"
    from controllers.types import Questions
    from tests.conftest import refused
    asking = Reports(record, actor=AGENT).create("Another choice", buttons=choice)
    assert f"report {asking.n} already asks this" in refused(lambda: Questions(record, actor=AGENT).create("Reshape it?", about=asking.ref, options=[{"title": "Yes", "text": "Reshape it"}, {"title": "No", "text": "Keep it"}], pick=1)), \
        "a row that asks through its buttons is not asked the same thing again as a question"
