"""Run the "Your turn" changes of steps 8 to 11 before the page promises anything.

    python your_turn.py rules [address]    change WHO, RULES and MOVES the way a student
                                           would, and ask the real model (33 questions)
    python your_turn.py numbers            change THINK, CREEP and DASH in hunt.py and step
                                           the gecko by hand (no model; windows flash)
    python your_turn.py decide [address]   change decide.py and run it (5 questions)
    python your_turn.py agent [address]    change agent.py and run its main() for real
                                           (29 questions)

"rules" asks the address in ask.py. Give another address to ask a server of your own
that takes the same request. "decide" and "agent" run the model itself where the
decider-ai library is installed, and ask that same address where it is not (see
agent_file.py).
The picks decide what the page may say. Two ideas failed and are not on the page: a move
and a rule for sleeping at night (in "rules"), and a wild gecko that always jumps (in "agent").
"""
import importlib.util
import math
import pathlib
import sys

from agent_file import load_agent

ASSETS = pathlib.Path(__file__).resolve().parent.parent      # the tutorial's files: one folder up from checks/
FOUR = ["The dragonfly is flying. It is too far to jump at.",
        "The dragonfly is flying. It is close enough to jump at.",
        "The dragonfly rests. It is too far to jump at.",
        "The dragonfly rests. It is close enough to jump at."]
LAZY_RULES = ("If the dragonfly is flying, stay still. "
              "If it rests and is too far to jump at, stay still. "
              "If it rests and is close enough, jump.")
# What the model picked for the four sentences on 2 October 2026, with the file as it is
# and with the lazy gecko's two changes. "numbers" uses these and asks nothing.
PICKS = dict(zip(FOUR, ["wait", "wait", "creep", "jump"]))
LAZY_PICKS = dict(zip(FOUR, ["wait", "wait", "wait", "jump"]))


def fresh(name):
    """A new copy of one tutorial file, so a change in one test cannot leak into the next."""
    spec = importlib.util.spec_from_file_location(name + "_copy", ASSETS / (name + ".py"))
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def show(title, ask_file, sees_list=FOUR):
    print(f"== {title}")
    for sees in sees_list:
        answer = ask_file.ask(sees)
        shares = " ".join(f"{move} {share:.2f}" for move, share in answer["probabilities"].items())
        print(f"   {answer['choice']:6} [{shares}]  {sees}")


def rules(address):
    def changed():
        ask_file = fresh("ask")
        if address:
            ask_file.MODEL = address
        return ask_file

    a = changed()
    show("as the file is", a)

    a = changed()
    a.RULES = ""
    show("step 8, Your turn 2: RULES is empty (the meanings in MOVES stay)", a)

    a = changed()
    del a.MOVES["creep"]
    show("step 8, Your turn 3: creep taken out of MOVES (RULES unchanged)", a)

    a = changed()
    a.RULES = LAZY_RULES
    show("step 8, Your turn 4, first half: a lazy gecko, only RULES changed", a)

    a = changed()
    a.RULES = LAZY_RULES
    a.MOVES["creep"] = "Creep closer. Never do this."
    show("step 8, Your turn 4, second half: RULES changed and the creep meaning says never", a)

    a = changed()
    a.WHO = "You are a dragonfly over a rice field. A gecko hunts you. It can only catch you when you rest."
    a.RULES = "If the gecko is close, fly away. If the gecko is far, rest."
    a.MOVES = {"fly": "Fly away. Do this when the gecko is close.", "rest": "Rest. Do this when the gecko is far."}
    show("step 8, Your turn 5: the other animal, a dragonfly with its own rules", a,
         ["The gecko is close.", "The gecko is far.", "The gecko is far. It creeps closer."])

    a = changed()
    a.RULES = a.RULES + " At night, sleep."
    a.MOVES["sleep"] = "Sleep. Do this at night."
    show("NOT on the page, it failed: a new move, sleep, and a new rule", a,
         FOUR + ["It is night. The dragonfly rests. It is close enough to jump at.",
                 "It is night. The dragonfly is flying. It is too far to jump at."])

    a = changed()
    a.MOVES = {"wait": "", "creep": "", "jump": ""}
    show("for the record: the meanings are empty (RULES stays)", a)


def step_by_hand(title, changes, picks=PICKS, frames=900):
    hunt = fresh("hunt")
    for name, value in changes.items():
        setattr(hunt, name, value)
    animal = hunt.Animal("model")
    asks = []

    def ask_model():
        asks.append(animal.frames)
        animal.doing = picks[animal.sees()]         # the answer at once, and no model

    animal.ask_model = ask_model
    start = math.dist(animal.points[0], animal.mouse)
    first = {}
    for frame in range(1, frames + 1):
        was_doing, was_target, was_caught = animal.doing, animal.target, animal.caught
        animal.hunt()
        animal.move_legs()
        if animal.doing == "creep" and was_doing != "creep":
            first.setdefault("creep starts", frame)
        if animal.target and not was_target:
            first.setdefault("jump starts", frame)
        if animal.caught > was_caught:
            first.setdefault("caught", frame)
    far = math.dist(animal.points[0], animal.mouse)
    animal.root.destroy()
    gaps = sorted({later - earlier for earlier, later in zip(asks, asks[1:])})
    print(f"== {title}")
    print(f"   starts {start:.0f} px away; first question at frame {asks[0]}; frames between questions {gaps}")
    print(f"   {first}; caught {animal.caught}; ends {far:.0f} px away")


def numbers():
    step_by_hand("as the file is", {})
    step_by_hand("step 9, Your turn 2: THINK = 200", {"THINK": 200})
    step_by_hand("step 9, Your turn 3, first half: CREEP = 4", {"CREEP": 4})
    step_by_hand("step 9, Your turn 3, second half: CREEP = 4 and DASH = 3", {"CREEP": 4, "DASH": 3})
    step_by_hand("step 9, Your turn 4: the lazy gecko's picks (it never creeps)", {}, LAZY_PICKS)


def agent(address):
    """Step 11. The file's text is changed, as a student would change it, and main() is run."""
    source = (ASSETS / "agent.py").read_text(encoding="utf-8")

    def run(title, old=None, new=None, words=None):
        changed = source
        if old:
            assert changed.count(old) == 1, old
            changed = changed.replace(old, new)
        names, how = load_agent(changed, address)
        if words:
            words(names)
        print(f"== {title}  (the model through the {how})")
        names["main"]()

    def wild(names):
        names["RULES"] = "Always jump."
        names["MOVES"]["creep"] = "Creep closer. Never do this."
        names["MOVES"]["jump"] = "Jump at it. Always do this."

    run("as the file is")
    run("step 11, Your turn 1: far = 9", "    far = 5 ", "    far = 9 ")
    run("step 11, Your turn 2: far = 12", "    far = 5 ", "    far = 12 ")
    run("step 11, Your turn 3: JUMP = 4", "JUMP = 2 ", "JUMP = 4 ")
    run('step 11, Your turn 4 and 5: the first sees sentence says "close enough"',
        '        sees = "It is too far to jump at."\n', '        sees = "It is close enough to jump at."\n')
    run("NOT on the page, it changed nothing: a wild gecko (the rule and both sentences say always jump)",
        words=wild)


def decide(address):
    """Step 10. decide.py's text is changed, as a student would change it, and run."""
    source = (ASSETS / "decide.py").read_text(encoding="utf-8")
    wait = ('"jump": "Jump at it. Do this when the dragonfly is close enough.",\n',
            '"jump": "Jump at it. Do this when the dragonfly is close enough.",\n'
            '        "wait": "Stay still. Do this when the dragonfly is flying.",\n')
    flying = ("Situation: It is close enough to jump at.", "Situation: The dragonfly is flying.")
    rule = ("If it is close enough, jump.", "If it is close enough, jump. If it is flying, stay still.")

    def run(title, swaps):
        changed = source
        for old, new in swaps:
            assert changed.count(old) == 1, old
            changed = changed.replace(old, new)
        print(f"== {title}")
        names, how = load_agent(changed, address, name="decide.py")
        print(f"   (the model through the {how})")

    run("as the file is", [])
    run("step 10, Your turn 1: the situation is too far",
        [("Situation: It is close enough to jump at.", "Situation: It is too far to jump at.")])
    run("step 10, Your turn 2: a third move, wait, and a flying dragonfly", [wait, flying])
    run("step 10, Your turn 3: and a rule for it", [wait, flying, rule])
    run("step 10, Your turn 4: print only the move",
        [("print(answer)", 'print(answer["answers"]["move"]["choice"])')])


def main():
    if len(sys.argv) > 1 and sys.argv[1] == "rules":
        rules(sys.argv[2] if len(sys.argv) > 2 else None)
    elif len(sys.argv) > 1 and sys.argv[1] == "numbers":
        numbers()
    elif len(sys.argv) > 1 and sys.argv[1] == "decide":
        decide(sys.argv[2] if len(sys.argv) > 2 else None)
    elif len(sys.argv) > 1 and sys.argv[1] == "agent":
        agent(sys.argv[2] if len(sys.argv) > 2 else None)
    else:
        print(__doc__)
        return 2
    return 0


if __name__ == "__main__":
    sys.exit(main())
