"""
vita.tour — a guided, hands-on first run.

vita.tour() walks you through the whole workflow one step at a time. It
explains each function and its arguments in plain English, shows the exact
command before running it, runs the real code on a lift (yours, if you give
it numbers), shows the real output, and pauses before moving on.

It is the guided companion to the other two helpers:
    vita.help()   the ideas
    vita.info()   the reference
    vita.tour()   the guided first run
"""

from __future__ import annotations

_RULE = "=" * 62
_THIN = "-" * 62


def _banner(title: str) -> None:
    print()
    print(_RULE)
    print(f"  {title}")
    print(_RULE)


def _indent(text: str, n: int = 2) -> str:
    pad = " " * n
    return "\n".join(pad + line for line in text.splitlines())


def tour(interactive: bool = True) -> None:
    """Take a guided first run through vita, one step at a time.

    vita.tour()                    a guided walkthrough (asks for your own lift)
    vita.tour(interactive=False)   the same walkthrough on a sample lift, no prompts
    """
    from . import calibrate, Workout  # lazy: package is fully loaded by call time

    state = {"interactive": interactive}

    def ask(prompt: str = ""):
        if not state["interactive"]:
            return None
        try:
            return input(prompt)
        except (EOFError, KeyboardInterrupt):
            state["interactive"] = False
            print(
                "\n  (No keyboard input here, so I'll run the rest on a sample lift.)"
            )
            return None

    def pause() -> bool:
        if not state["interactive"]:
            return True
        r = ask("\n  Press Enter to keep going (or type q to stop): ")
        if r is not None and r.strip().lower() in {"q", "quit", "exit", "stop"}:
            print("\n  Stopped here. Run vita.tour() any time to start over.\n")
            return False
        return True

    def num(prompt: str, default):
        r = ask(prompt)
        if r is None or r.strip() == "":
            return default
        try:
            v = float(r)
            return int(v) if v == int(v) else v
        except ValueError:
            print(f"      (Couldn't read that as a number, so I'll use {default}.)")
            return default

    def text(prompt: str, default: str) -> str:
        r = ask(prompt)
        if r is None or r.strip() == "":
            return default
        return r.strip()

    # -- intro ---------------------------------------------------------------
    _banner("Welcome to vita")
    print(
        "\n"
        "  vita is a training coach, and the whole idea is small:\n\n"
        "    1. Teach it ONE of your lifts, one time.   ->  you get a Lift\n"
        "    2. Start a training day.                    ->  you get a Workout\n"
        "    3. Ask the Workout questions, and log sets to it.\n\n"
        "  Almost every command takes the same two things: a weight, and either\n"
        "  how many reps, or how many reps you want to leave in the tank (that is\n"
        "  RIR, short for 'reps in reserve').\n\n"
        "  I'll walk you through one real session. I'll show you each command\n"
        "  before I run it, so you can see exactly what to type."
    )
    if state["interactive"]:
        print(
            "\n  Use your own numbers, or just press Enter to take the sample values."
        )
    if not pause():
        return

    # -- step 1: calibrate ---------------------------------------------------
    _banner("Step 1 of 5:  teach vita a lift   ->   vita.calibrate(...)")
    print(
        "\n"
        "  Give vita two HARD sets of one exercise, at two different weights.\n"
        "  From those it works out your whole strength curve. It takes four\n"
        "  numbers and a name:\n\n"
        "     weight1, reps1   your first hard set   (e.g. 5 reps at 315)\n"
        "     weight2, reps2   a second hard set at a different weight\n"
        "     name             whatever you want to call the lift\n"
    )
    name = text("    Name of the lift [Bench]: ", "Bench")
    w1 = num("    First set,  weight [315]: ", 315)
    r1 = num("    First set,  reps   [5]: ", 5)
    w2 = num("    Second set, weight [275]: ", 275)
    r2 = num("    Second set, reps   [9]: ", 9)

    line = f'lift = vita.calibrate(weight1={w1}, reps1={r1}, weight2={w2}, reps2={r2}, name="{name}")'
    print(f"\n  The command:\n    >>> {line}\n")
    lift = calibrate(weight1=w1, reps1=r1, weight2=w2, reps2=r2, name=name)
    print("  What it gives back:")
    print(_indent(repr(lift), 4))
    print(
        "\n"
        "  That is a Lift. 'confidence' says how much to trust it: two sets far\n"
        "  apart in weight give a sharper read than two close together. You only\n"
        "  do this ONCE per exercise."
    )
    if not pause():
        return

    # -- step 2: workout -----------------------------------------------------
    _banner("Step 2 of 5:  start a training day   ->   vita.Workout(lift)")
    print(
        "\n"
        "  vita.Workout(lift) starts a session. You hand it the Lift you just\n"
        "  made. (You can also pass rest= in seconds; it defaults to 180.)\n\n"
        "  The command:\n"
        "    >>> w = vita.Workout(lift)\n"
    )
    w = Workout(lift)
    print("  Done. 'w' is your Workout. Every question from here is a method on w:")
    print("  you type w.something(...). Let's use it.")
    if not pause():
        return

    working = w2  # the lighter set has more reps to play with
    guess = max(1, round(r2 * 0.6))

    # -- step 3: predict_set -------------------------------------------------
    _banner("Step 3 of 5:  how hard is a set?   ->   w.predict_set(...)")
    print(
        "\n"
        "  BEFORE doing a set, ask what it will cost. Two arguments: the weight,\n"
        "  and how many reps you are thinking of doing.\n\n"
        f"  The command:\n    >>> w.predict_set(weight={working}, reps={guess})\n"
    )
    pred = w.predict_set(weight=working, reps=guess)
    print(_indent(repr(pred), 2))
    print(
        "\n"
        "  Reading it: 'Predicted RIR' is how many reps it thinks you'd have left,\n"
        "  'RPE' is effort out of 10 (10 = failure), and 'Status' plus 'Coach' are\n"
        "  the plain-English read. Nothing was logged; this was just a what-if."
    )
    if not pause():
        return

    # -- step 4: target_reps -------------------------------------------------
    _banner("Step 4 of 5:  how many reps should I do?   ->   w.target_reps(...)")
    print(
        "\n"
        "  Flip it around. Give the weight and how many reps you want to LEAVE in\n"
        "  the tank (rir), and it tells you how many to do right now.\n\n"
        f"  The command:\n    >>> w.target_reps(weight={working}, rir=2)\n"
    )
    n = w.target_reps(weight=working, rir=2)
    print(f"    {n}")
    print(
        f"\n  So at {working}, stopping with 2 reps in reserve means about {n} reps.\n"
        "  Let's log that as the set you did."
    )
    if not pause():
        return

    # -- step 5: log_set + build_plan ----------------------------------------
    _banner(
        "Step 5 of 5:  log a set, then plan the rest   ->   w.log_set(...), w.build_plan(...)"
    )
    print(
        "\n"
        "  Record what you actually did with w.log_set(weight, reps). This is the\n"
        "  ONE command that changes things: your fatigue goes up, so later sets\n"
        "  know you are more tired.\n\n"
        f"  The command:\n    >>> w.log_set(weight={working}, reps={n})\n"
    )
    logged = w.log_set(weight=working, reps=n)
    print(_indent(repr(logged), 2))
    print(
        "\n  Notice 'Capacity now' dropped below 100%. That is fatigue being tracked."
    )

    print(
        "\n"
        "  Now plan out your sets. Give a weight, the RIR to hold, and how many\n"
        "  sets, and it simulates the fatigue set by set.\n\n"
        f"  The command:\n    >>> w.build_plan(weight={working}, target_rir=2, sets=4)\n"
    )
    plan = w.build_plan(weight=working, target_rir=2, sets=4)
    print(_indent(repr(plan), 2))
    print(
        "\n"
        "  Want a ramp with a different weight each set instead? Pass a list of\n"
        "  weights (and, if you like, a rep count for each):\n"
        "    >>> w.build_plan(weights=[135, 185, 225, 275], reps=[8, 6, 4, 3])"
    )
    if not pause():
        return

    # -- wrap-up -------------------------------------------------------------
    _banner("That's the whole loop")
    print(
        "\n"
        "  You just ran the entire workflow:\n\n"
        "    lift = vita.calibrate(...)       teach a lift (once per exercise)\n"
        "    w    = vita.Workout(lift)        start a training day\n"
        "    w.predict_set(weight, reps)      how hard is this set?\n"
        "    w.target_reps(weight, rir)       how many reps for a target RIR?\n"
        "    w.log_set(weight, reps)          record a set (updates fatigue)\n"
        "    w.build_plan(weight, ...)        plan your sets (weights=[...] for a ramp)\n\n"
        "  Want the details of any one command, like every argument it takes?\n"
        '     vita.info("calibrate")      zoom in on one function\n'
        '     vita.help("planning")       learn a concept\n\n'
        "  For pull-ups, dips, and other bodyweight moves there's also\n"
        "  vita.calibrate_bodyweight(max_reps, name=...).\n\n"
        "  Now try it with your own next lift. Run vita.tour() again any time."
    )
    print()
