"""Companion to probe_discount.py: lens table over the PROMPT positions
after k=2 prefill settling (arm A adapter) — what the workspace holds
about the question before any pause/answer compute begins."""
import os
import sys
from pathlib import Path

import torch

from carry_common import _pos_ids, prompt_prefill
from loop_common import BandLooper, MergeAdapter, chat_prompt, DIRECT_SUFFIX

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from jlens.core import JLens, ResidualCapture, load_model  # noqa: E402

OUT = Path(os.environ.get("LOOP_OUT",
    Path(__file__).resolve().parent.parent / "results-loop"))
Q = "A shirt costs $80 and is on sale for 15% off. How much does it cost?"
K = 2
TAG = "discount"
if "--idx" in sys.argv:
    import json
    _i = int(sys.argv[sys.argv.index("--idx") + 1])
    _star = {it["idx"]: it for it in json.load(open(OUT / "star_data.json"))
             if it["split"] == "test"}
    Q = _star[_i]["question"]
    TAG = f"gsm{_i}"

model, tok = load_model(dtype=torch.bfloat16)
looper = BandLooper(model)
band = list(range(looper.l0, looper.l1 + 1))
adapter = MergeAdapter(d=model.config.get_text_config().hidden_size).cuda()
adapter.load_state_dict(torch.load(OUT / "adapter_carrycot_e400.pt",
                                   map_location="cuda"))
jbar = torch.load(Path(__file__).resolve().parent.parent / "results/jbar.pt",
                  map_location="cuda")["Jbar"]
jl = JLens(model, tok, jbar)

prompt = chat_prompt(tok, Q, DIRECT_SUFFIX)
ids = tok(prompt, add_special_tokens=False,
          return_tensors="pt")["input_ids"].cuda()
mask = torch.ones_like(ids)

with torch.no_grad():
    calls, _ = looper.capture(ids, mask, logits_to_keep=1,
                              position_ids=_pos_ids(mask))
    e = looper._hin[looper.l0]
    pmask = torch.ones_like(ids, dtype=torch.bool)
    S, X = prompt_prefill(looper, adapter, e, calls, pmask, K)
    with ResidualCapture(model, layers=band) as rc:
        looper.band(X, calls)
    acts = {l: rc.acts[l][0] for l in band}
    # k=0 reference: plain forward, no adapter anywhere
    with ResidualCapture(model, layers=band) as rc0:
        model(input_ids=ids, attention_mask=mask, use_cache=False)
    acts0 = {l: rc0.acts[l][0] for l in band}

table = {}
for l in band:
    idx, p = jl.read(acts[l], l, topk=5)
    table[l] = (idx.cpu(), p.cpu())
idx0, p0 = jl.read(acts0[30], 30, topk=5)
toks = [repr(tok.decode([t])) for t in ids[0].tolist()]
torch.save({"question": Q, "k": K, "poslab": toks, "band": band,
            "table": table, "base30": (idx0.cpu(), p0.cpu())},
           OUT / f"probe_{TAG}_prompt.pt")
show = [14, 18, 22, 26, 30]
print(f"=== prompt board after k={K} settle (arm A)  ||  k=0 base ===")
for i, lab in enumerate(toks):
    row, row0 = [], []
    for l in show:
        idx, _ = jl.read(acts[l][i][None], l, topk=3)
        row.append("/".join(repr(tok.decode([t]))[1:-1]
                            for t in idx[0].tolist()))
    idx0, _ = jl.read(acts0[30][i][None], 30, topk=3)
    base30 = "/".join(repr(tok.decode([t]))[1:-1] for t in idx0[0].tolist())
    print(f"{i:3d} {lab:14s} " + "  ".join(
        f"L{l}:{r}" for l, r in zip(show, row)) + f"   || base-L30:{base30}",
        flush=True)
