"""
vita._help — the in-library guide

Exposed as ``vita.help``. Call ``vita.help()`` for an overview, or
``vita.help("calibration" | "workout" | "planning" | "coaching" | "modes")``
for a focused walkthrough. Pure presentation: it prints and returns None.
"""

from __future__ import annotations

_OVERVIEW = """\
VITA — Vivanco Intelligent Training Assistant  (v{version})

VITA estimates your reps-in-reserve on every set, tracks fatigue across a
session, and prescribes your next sets.

Quickstart
  import vita
  bench = vita.calibrate(weight1=315, reps1=5, weight2=275, reps2=9, name="Bench")
  w = vita.Workout(bench)
  w.predict_set(weight=295, reps=5)     # what will this set cost?
  w.target_reps(weight=275, rir=2)      # how many reps for 2 in reserve?
  w.log_set(weight=275, reps=7)         # log it; get a coaching readout back
  w.build_plan(weight=275, target_rir=2, sets=5)
  w.finish()                            # end-of-session summary

More help  (call vita.help("...") )
  "calibration"   building a lift from your all-out sets
  "workout"       predicting, logging, and reading sets
  "planning"      the three plan modes
  "coaching"      RIR, RPE, and the coaching labels
  "modes"         quiet / summary / verbose / developer output

Every function in plain English:  vita.info()
New here? Take the guided tour:   vita.tour()
"""

_CALIBRATION = """\
Calibration — turning all-out sets into a lift

Two all-out sets at DIFFERENT loads identify your load-rep curve:
  bench = vita.calibrate(weight1=315, reps1=5, weight2=275, reps2=9, name="Bench")

More sets give a more confident curve — use the builder:
  c = vita.Calibration("Bench")
  c.add_set(315, 5); c.add_set(275, 9); c.add_set(225, 14)
  bench = c.fit()
  bench.confidence     # "high" / "medium" / "low"
  bench.report         # quality, R², load spread, any flagged sets

Tips
  - Spread your loads: a heavier AND a lighter set sharpen the estimate.
  - A fat-fingered entry is caught as an outlier; fit(strict=False) drops it.
  - Bodyweight movement?  vita.calibrate_bodyweight(max_reps, name="Pull-ups")
"""

_WORKOUT = """\
Workout — your live session

  w = vita.Workout(bench)             # optionally rest=, output=
  w.predict_set(weight=295, reps=5)  # the RIR this set will leave, before you do it
  w.target_reps(weight=275, rir=2)   # reps to hit a target RIR right now
  w.log_set(weight=275, reps=7)      # record a set; capacity updates
  w.last_recommendation              # the coach's note on your last set
  w.summary() / w.finish()           # end-of-session readout

Each call returns a readout object. In a notebook or REPL it shows its coaching
block automatically; print(obj) always shows it too.
"""

_PLANNING = """\
Planning — build today's sets

  w.build_plan(weight=275, target_rir=2, sets=5)    # mode="rir" (default)

Modes
  "rir"      hold a target RIR each set; reps fall as fatigue builds
  "fixed"    hold the reps (e.g. reps=8); the predicted RIR falls across sets
  "topset"   one top set to target_rir, then back-off sets at a lighter load
             (backoff_pct=10 by default)

The plan rounds reps for display but simulates fatigue at full precision, so the
rep targets never tick back up between sets.
"""

_COACHING = """\
Coaching — reading your effort

RIR (reps in reserve) is how many reps you had left. RPE is the flip side:
RPE = 10 - RIR, so 0 RIR = RPE 10 = failure.

VITA labels each set by how close to failure it is:
  Warm-up - Easy - Moderate - Hard working set - Near failure - Failure - Past failure

Most growth-driving working sets sit in the 1-3 RIR band. Every prediction and
logged set carries .status, .rpe, and .advice.
  vita.coaching.classify(2.0)        # inspect any RIR value
"""

_MODES = """\
Output modes — how results display on their own

  vita.set_output("verbose")   # quiet - summary (default) - verbose - developer

  quiet       one concise line
  summary     the coaching block (default)
  verbose     the block plus extra detail
  developer   raw, full-precision numbers

print(obj) always shows the full summary regardless of mode. Set it per-workout
with vita.Workout(bench, output="quiet").
"""

_TOPICS = {
    "calibration": _CALIBRATION,
    "calibrate": _CALIBRATION,
    "workout": _WORKOUT,
    "session": _WORKOUT,
    "planning": _PLANNING,
    "plan": _PLANNING,
    "coaching": _COACHING,
    "rir": _COACHING,
    "rpe": _COACHING,
    "modes": _MODES,
    "output": _MODES,
}


def help(topic: str = None) -> None:
    """Print a guide to VITA.

    vita.help()               -> overview + the list of topics
    vita.help("planning")     -> a focused walkthrough of one topic
    """
    if topic is None:
        from . import __version__  # lazy: __init__ is fully loaded by call time

        print(_OVERVIEW.format(version=__version__))
        return

    text = _TOPICS.get(topic.lower().strip())
    if text is None:
        names = ", ".join(sorted(set(_TOPICS)))
        print(
            f"No help topic {topic!r}.\n"
            f"Try one of: {names}\n"
            f"Or call vita.help() for an overview."
        )
        return
    print(text)


# --------------------------------------------------------------------------
# vita.info() — a plain-English list of every tool
# --------------------------------------------------------------------------
# (group, call, what it does, example, what you get back)
_INFO = [
    (
        "Getting help",
        "vita.help(...)",
        "A guided tour of the ideas behind vita.",
        'vita.help("planning")',
        "",
    ),
    (
        "Getting help",
        "vita.info(...)",
        "This list. Pass a name to zoom in on one tool.",
        'vita.info("log_set")',
        "",
    ),
    (
        "Set up a lift",
        "vita.calibrate(...)",
        "Build a lift from two all-out sets at different weights.",
        'bench = vita.calibrate(weight1=315, reps1=5, weight2=275, reps2=9, name="Bench")',
        "a Lift you train with",
    ),
    (
        "Set up a lift",
        "vita.Calibration(...)",
        "Start a builder when you have more than two sets.",
        'c = vita.Calibration("Squat")',
        "a builder",
    ),
    (
        "Set up a lift",
        ".add_set(...)",
        "Add one all-out set to the builder (chain as many as you like).",
        "c.add_set(315, 10)",
        "the builder, so you can keep chaining",
    ),
    (
        "Set up a lift",
        ".fit(...)",
        "Finish the builder: fit the lift and score it. strict=False drops a bad set and refits.",
        "squat = c.fit()",
        "a Lift with a confidence read",
    ),
    (
        "Set up a lift",
        "vita.calibrate_bodyweight(...)",
        "Calibrate a bodyweight move from one max-effort set.",
        'pull = vita.calibrate_bodyweight(20, name="Pull-ups")',
        "a Lift",
    ),
    (
        "Check a lift",
        "lift.one_rep_max",
        "Your estimated one-rep max.",
        "bench.one_rep_max",
        "",
    ),
    (
        "Check a lift",
        "lift.confidence",
        'How much to trust the calibration: "high", "medium", or "low".',
        "bench.confidence",
        "",
    ),
    (
        "Check a lift",
        "lift.quality",
        "The same trust as a 0-1 score.",
        "bench.quality",
        "",
    ),
    (
        "Check a lift",
        "lift.report",
        "The full read: quality, fit, load spread, and any flagged sets.",
        "print(bench.report)",
        "",
    ),
    (
        "Run a session",
        "vita.Workout(...)",
        "Start a training session for a lift.",
        "w = vita.Workout(bench)",
        "a Workout",
    ),
    (
        "Run a session",
        "w.predict_set(...)",
        "Before a set: the RIR and effort it will cost you.",
        "w.predict_set(weight=295, reps=5)",
        "a prediction readout",
    ),
    (
        "Run a session",
        "w.target_reps(...)",
        "How many reps to leave a chosen RIR, right now.",
        "w.target_reps(weight=275, rir=2)",
        "a rep count",
    ),
    (
        "Run a session",
        "w.log_set(...)",
        "Record a set you did; your fatigue updates.",
        "w.log_set(weight=275, reps=7)",
        "a set readout with coaching",
    ),
    (
        "Run a session",
        "w.capacity",
        "How fresh you are right now (1.0 = fully fresh).",
        "w.capacity",
        "",
    ),
    ("Run a session", "w.last_set", "The last set you logged.", "w.last_set", ""),
    (
        "Run a session",
        "w.last_recommendation",
        "The coaching note on your last set.",
        "w.last_recommendation",
        "",
    ),
    (
        "Run a session",
        "w.finish()",
        "The end-of-session summary (same as w.summary()).",
        "w.finish()",
        "a session summary",
    ),
    (
        "Read a set you logged",
        "set.status",
        'A plain label for how close to failure it was (e.g. "Hard working set").',
        "r = w.log_set(275, 7); r.status",
        "",
    ),
    (
        "Read a set you logged",
        "set.rpe",
        "The set's RPE, where 10 means failure.",
        "r.rpe",
        "",
    ),
    (
        "Read a set you logged",
        "set.advice",
        "What to do about this set.",
        "r.advice",
        "",
    ),
    (
        "Read a set you logged",
        "set.coaching",
        "Everything above in one object (label, rpe, zone, note, advice).",
        "r.coaching",
        "",
    ),
    (
        "Read a set you logged",
        "print(set)",
        "The full coaching block for the set.",
        "print(r)",
        "",
    ),
    (
        "Plan your sets",
        "w.build_plan(...)",
        "Prescribe your sets for today. Pick a mode below.",
        "w.build_plan(weight=275, target_rir=2, sets=5)",
        "a plan table",
    ),
    (
        "Plan your sets",
        'mode="rir"',
        "Hold a target RIR every set; reps fall as you tire (the default).",
        "w.build_plan(weight=275, target_rir=2, sets=5)",
        "",
    ),
    (
        "Plan your sets",
        'mode="fixed"',
        "Hold the reps every set; the predicted RIR falls.",
        'w.build_plan(weight=275, reps=8, sets=5, mode="fixed")',
        "",
    ),
    (
        "Plan your sets",
        'mode="topset"',
        "One top set, then lighter back-off sets.",
        'w.build_plan(weight=300, target_rir=1, mode="topset")',
        "",
    ),
    (
        "Plan your sets",
        "plan.to_frame()",
        "The plan as a pandas table, to save or chart.",
        "w.build_plan(weight=275).to_frame()",
        "a DataFrame",
    ),
    (
        "Control the output",
        "vita.set_output(...)",
        'How results show themselves: "quiet", "summary", "verbose", or "developer".',
        'vita.set_output("verbose")',
        "",
    ),
    (
        "Control the output",
        "vita.get_output()",
        "The current output mode.",
        "vita.get_output()",
        "",
    ),
    (
        "Fit the model to your data",
        "vita.observed_session(...)",
        "Turn logged rows (load, reps, rir[, rest]) into a session.",
        "s = vita.observed_session([(275, 8, 2.0, 180), (275, 7, 1.0)])",
        "a session",
    ),
    (
        "Fit the model to your data",
        "vita.fit_fatigue(...)",
        "Fit the fatigue model to your own logged sessions.",
        "fit = vita.fit_fatigue(bench, sessions)",
        "a FatigueFit",
    ),
    (
        "Fit the model to your data",
        "fit.validated",
        "Did the fitted model beat the defaults on held-out sessions?",
        "fit.validated",
        "",
    ),
    (
        "Fit the model to your data",
        "fit.params",
        "The fitted numbers - hand them to a Workout.",
        "w = vita.Workout(bench, params=fit.params)",
        "",
    ),
    (
        "Fit the model to your data",
        "vita.coverage(...)",
        "Diagnostic: how often real RIR lands inside the model range.",
        "vita.coverage(bench, sessions, fit.params)",
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.Params(...)",
        "The fatigue dial values (usually fitted, not set by hand).",
        "vita.Params(k=0.4, gamma=2.0, t_rec=180)",
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.total_load(...)",
        "Combine bodyweight and added weight into one load.",
        'vita.total_load(200, vita.BODYWEIGHT_FRACTION["pullup"], added=45)',
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.rir_interval(...)",
        "An RIR estimate with an uncertainty range, not just a number.",
        "vita.rir_interval(bench.curve, reps=5, load=295)",
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.LoadRepCurve",
        "The raw load-to-reps curve behind a lift.",
        "bench.curve",
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.Session",
        "The bare VPFM engine, one set at a time.",
        "vita.Session(bench.curve)",
        "",
    ),
    (
        "Advanced - the VPFM engine",
        "vita.BODYWEIGHT_FRACTION",
        "How much bodyweight each move loads (pull-up = 1.0).",
        'vita.BODYWEIGHT_FRACTION["squat"]',
        "",
    ),
]


def _info_full(call: str, what: str, how: str, gives: str) -> str:
    lines = [f"  {call}", f"      {what}"]
    if how:
        lines.append(f"      \u2192 {how}")
    if gives:
        lines.append(f"      gives: {gives}")
    return "\n".join(lines)


def info(name: str = None) -> None:
    """A plain-English list of every vita tool: what it does and how to call it.

    vita.info()             the whole list, grouped by what you're doing
    vita.info("log_set")    zoom in on one tool (name or partial name)
    """
    if name is not None:
        q = name.strip().lower().lstrip(".")
        if q.startswith("vita."):
            q = q[5:]
        hits = [e for e in _INFO if q in e[1].lower()]
        if not hits:
            print(
                f"No vita tool matching {name!r}. Run vita.info() for the whole list."
            )
            return
        print()
        for _g, call, what, how, gives in hits:
            print(_info_full(call, what, how, gives))
            print()
        return

    from . import __version__

    out = [
        f"VITA - what every piece does  (v{__version__})",
        "",
        "The flow:  calibrate a lift  ->  make a Workout  ->  predict / log / plan sets.",
        "New here? Run vita.tour() for a hands-on, guided walkthrough.",
        'Zoom in with vita.info("log_set").  Learn the ideas with vita.help().',
    ]
    group = None
    for g, call, what, _how, _gives in _INFO:
        if g != group:
            out.append("")
            out.append(f"-- {g} --")
            group = g
        out.append(f"  {call:<31}{what}")
    out.append("")
    print("\n".join(out))
