"""試算 v2.8（レジの数を条件に足した版）の結果を、公開中の試算のページに入れる（2026-10-25）。
使い方: python3 build_page_v28.py <公開中のページ> <v2.8 の results.json> <出力するページ>
- const D：条件に registers（レジの数）を足し、結果に pay_wait_mean（食べ終えてから会計まで）を足す。店の情報・平面図は公開中のまま、レジの数だけ足す
- 画面：条件に「レジ」を足す。結果に「食べ終えてから会計まで」を足す。前提に会計とレジの決まりを足す。見出しの版を v2.8 にする
- 費用の欄・LP 用の色・使われ方の記録は公開中のまま"""
import json, re, sys
import haizen_sim as h

live, res, out = sys.argv[1:4]
s = open(live, encoding="utf-8").read()
N = json.load(open(res, encoding="utf-8"))
assert N["common"] == json.loads(json.dumps(h.COMMON)), "結果が今の haizen_sim.py の前提と違う"
assert N["common"]["model"] == "v2.8"

getD = lambda t: [l for l in t.split("\n") if l.startswith("const D = ")]
ol = getD(s); assert len(ol) == 1
D = json.loads(ol[0][len("const D = "):].rstrip(";"))
old_k = D["k"]
assert old_k[:7] == ["store", "staff", "robots", "arrivals_per_h", "mode", "trays", "fragile_by"] and old_k[-1] == "ng"
cond = old_k[:7] + ["registers"]
mets = old_k[7:-1] + ["pay_wait_mean"]
D["k"] = cond + mets + ["ng"]
conds = list(h.conditions())
assert len(N["rows"]) == len(conds)
rows = []
for c, r in zip(conds, N["rows"]):
    assert all(r[k] == c[k] for k in cond), ("条件の並びが違う", c, r)
    rows.append([r[k] for k in cond] + [round(r[k], 3) for k in mets] + [1 if r["staff_wait_mean"] >= h.NG_MIN else 0])
D["r"] = rows
for k, st in D["stores"].items():
    st["registers"] = h.STORES[k]["registers"]
    st["registers_all"] = h.STORES[k]["registers_all"]
D["common"] = N["common"]
s = s.replace(ol[0], "const D = " + json.dumps(D, ensure_ascii=False, separators=(",", ":")) + ";")


def rep(a, b, n=1):
    global s
    assert s.count(a) == n, (a[:80], s.count(a))
    s = s.replace(a, b)


# 見出しの版
assert len(re.findall(r"試作 v2\.\d+", s)) == 1
s = re.sub(r"試作 v2\.\d+", "試作 v2.8", s)

# 条件の欄に「レジ」
rep('''        <div class="seg" id="seg-arr"></div>
        <p class="note" id="arr-note"></p>
      </fieldset>''', '''        <div class="seg" id="seg-arr"></div>
        <p class="note" id="arr-note"></p>
      </fieldset>
      <fieldset class="ctl">
        <legend>レジ</legend>
        <div class="seg" id="seg-reg"></div>
        <p class="note" id="reg-note"></p>
      </fieldset>''')

# 選んでいる条件・データの引き方
rep('''const state = { store: "family", staff: 3, robots: 2, mode: "serve", crowd: 1, trays: 3, fb: "any", cbase: 3, crob: 2 };''',
    '''const state = { store: "family", staff: 3, robots: 2, mode: "serve", crowd: 1, trays: 3, fb: "any", cbase: 3, crob: 2, reg: D.stores.family.registers };''')
rep('''const IDX = new Map(ROWS.map(r => [[r.store, r.staff, r.robots, r.arrivals_per_h, r.mode, r.trays, r.fragile_by].join("|"), r]));
function find(store, staff, robots, arr, mode, trays = state.trays, fb = state.fb) {
  const k = robots === 0 ? [store, staff, 0, arr, "serve", 1, "any"] : [store, staff, robots, arr, mode, trays, fb];''',
    '''const IDX = new Map(ROWS.map(r => [[r.store, r.staff, r.robots, r.arrivals_per_h, r.mode, r.trays, r.fragile_by, r.registers].join("|"), r]));
// レジの数（v2.8）。指定がなければ左の条件のまま
function find(store, staff, robots, arr, mode, trays = state.trays, fb = state.fb, reg = state.reg) {
  const k = robots === 0 ? [store, staff, 0, arr, "serve", 1, "any", reg] : [store, staff, robots, arr, mode, trays, fb, reg];''')
# 店を選び直したら、レジはその店の最初の数に戻す
rep('''state.cbase = S().staff[1];''', '''state.cbase = S().staff[1]; state.reg = S().registers;''', 2)
rep('''  seg("seg-arr", "arr", s.arrivals.map((v, i) => ({ v: i, l: `${CROWD[i]}` })), () => state.crowd, v => state.crowd = v);''',
    '''  seg("seg-arr", "arr", s.arrivals.map((v, i) => ({ v: i, l: `${CROWD[i]}` })), () => state.crowd, v => state.crowd = v);
  if (!s.registers_all.includes(state.reg)) state.reg = s.registers;
  seg("seg-reg", "reg", s.registers_all.map(v => ({ v, l: `${v}つ` })), () => state.reg, v => state.reg = v);''')

# 結果
rep('''const METRICS = [
  { key: "entry_wait_mean", label: "入店待ち（平均）", plain: plainMin },
  { key: "turn_mean", label: "客が帰ってから卓が使えるまで", plain: plainMin },
  { key: "dish_wait_mean", label: "料理ができてから届くまで", plain: plainSec },
  { key: "walk_km_per_staff", label: "スタッフ1人が歩く距離", plain: v => `${v.toFixed(2)}km` },
  { key: "staff_util", label: "スタッフの手がふさがっている割合", plain: plainPct },
];''', '''const METRICS = [
  { key: "entry_wait_mean", label: "入店待ち（平均）", plain: plainMin },
  { key: "turn_mean", label: "客が帰ってから卓が使えるまで", plain: plainMin },
  { key: "dish_wait_mean", label: "料理ができてから届くまで", plain: plainSec },
  { key: "walk_km_per_staff", label: "スタッフ1人が歩く距離", plain: v => `${v.toFixed(2)}km` },
  { key: "staff_util", label: "スタッフの手がふさがっている割合", plain: plainPct },
  { key: "pay_wait_mean", label: "食べ終えてから会計が始まるまで", plain: plainMin },
];''')
# 「レジが詰まる」の印（v2.8）：食べ終えてから会計が始まるまでが NG_MIN 分以上。「店が回らない」とは別に出す（2026-10-25 エイイチと決めた）
rep('''const cellOf = (m, r) => m.key === "entry_wait_mean" ? seatCell(r) : m.plain(r[m.key]);''',
    '''const regJam = r => r.pay_wait_mean >= NG_MIN;
const regTag = r => `<span class="ng">レジが詰まる</span>`;
const cellOf = (m, r) => m.key === "entry_wait_mean" ? seatCell(r)
  : m.key === "pay_wait_mean" ? m.plain(r[m.key]) + (regJam(r) ? `<span class="sm">${regTag(r)}</span>` : "")
  : m.plain(r[m.key]);''')
rep('''    `${s.label}では、空いている ${s.arrivals[0]}組/時・普通 ${s.arrivals[1]}組/時・混雑 ${s.arrivals[2]}組/時。混む時間は${s.peak_min}分だけ続く前提です（ランチ・ディナーのピーク）。`;''',
    '''    `${s.label}では、空いている ${s.arrivals[0]}組/時・普通 ${s.arrivals[1]}組/時・混雑 ${s.arrivals[2]}組/時。混む時間は${s.peak_min}分だけ続く前提です（ランチ・ディナーのピーク）。`;
  document.getElementById("reg-note").textContent =
    `会計はレジ1つにつき1組ずつ（1組${D.common.pay_s}秒）。レジ${state.reg}つなら1時間に${Math.floor(3600 * state.reg / D.common.pay_s)}組までです。${s.label}は${s.registers}つを最初に見せています。`;''')
rep('''  document.getElementById("cond-sub").textContent = `${CROWD[state.crowd]}（${arr}組/時）` + ''',
    '''  document.getElementById("cond-sub").textContent = `${CROWD[state.crowd]}（${arr}組/時）・レジ${state.reg}つ` + ''')
rep('''    { k: "スタッフの手がふさがっている割合", v: pct(r.staff_util), h: "9割を超えると呼び出しに出られない" },''',
    '''    { k: "スタッフの手がふさがっている割合", v: pct(r.staff_util), h: "9割を超えると呼び出しに出られない" },
    { k: "食べ終えてから会計が始まるまで", v: `${fmtMin(r.pay_wait_mean)}${regJam(r) ? regTag(r) : ""}`, h: `レジの順番とスタッフの手が空くのを待つ。そのあいだ卓はふさがったまま（レジ${state.reg}つ）` + (regJam(r) ? `。平均${NG_MIN}分以上待つので「レジが詰まる」` : "") },''')

# 店ごとの比べ：レジは各店の最初の数
rep('''    `各店のスタッフは真ん中の人数・混み具合は普通・運び方は左の条件のままで、ロボット2台を入れたときの入店待ち（上）と、スタッフ1人が歩く距離（下）の変化です。`;''',
    '''    `各店のスタッフは真ん中の人数・混み具合は普通・レジは各店の最初の数（大型店は2つ、ほかは1つ）・運び方は左の条件のままで、ロボット2台を入れたときの入店待ち（上）と、スタッフ1人が歩く距離（下）の変化です。`;''')
rep('''    const z = find(k, st, 0, arr, "serve");
    const rs = MODES.map(o => find(k, st, 2, arr, o.v));''',
    '''    const z = find(k, st, 0, arr, "serve", undefined, undefined, s.registers);
    const rs = MODES.map(o => find(k, st, 2, arr, o.v, undefined, undefined, s.registers));''')

# 前提
rep('''    ["案内・会計・呼び出し対応", `${c.guide_s}秒・${c.pay_s}秒・${c.call_s}秒（歩く時間は別）`],''',
    '''    ["案内・会計・呼び出し対応", `${c.guide_s}秒・${c.pay_s}秒・${c.call_s}秒（歩く時間は別）`],
    ["「レジが詰まる」の決め方", `食べ終えてから会計が始まるまでが、平均${NG_MIN}分以上の条件。「店が回らない」とは別の印で、「店が回らない」の決め方は変えていない（v2.8）`],
    ["会計とレジ", `会計はレジ1つにつき1組ずつ。食べ終えた組は卓に座ったままレジの順番を待ち、順番が来たらスタッフの手が空くのを待って会計する。レジ1つなら1時間に${Math.floor(3600 / c.pay_s)}組まで。レジの数は1つ・2つを選べる（v2.8 から。それまでは、会計をスタッフのだれでも同時にできた）`],''')

open(out, "w", encoding="utf-8").write(s)
print(len(s), "bytes", len(rows), "rows")
