{{define "backups_escrow"}}
{{template "layout_start" .}}

<!-- Controller-driven escrow ceremony wizard (v0.127.0). R (the recovery code) arrives ONLY via
     the claim XHR and lives ONLY in this page's JS scope — it is never templated server-side,
     never sent anywhere else, and the page + claim response are Cache-Control: no-store. -->

<div class="page-header">
    <div style="display:flex;align-items:center;gap:.5rem">
        <a href="/backups/remote" class="btn btn-sm btn-outline">← Vissza</a>
        <h2>Helyreállítási kód</h2>
    </div>
    <span class="domain-badge">{{.Domain}}</span>
</div>

{{if not .AgentSupported}}
<div class="alert alert-info">A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik. Próbálja újra később.</div>
{{else}}

<!-- 1. Preflight -->
<div class="settings-card" id="pf-card">
    <h3>1. Előfeltételek ellenőrzése</h3>
    <div id="pf-error" class="alert alert-error" style="display:none;margin-bottom:1rem"></div>
    <div id="pf-list"><p class="form-hint">Ellenőrzés folyamatban…</p></div>
</div>

<!-- 2. Warning + 3. Re-auth (revealed when preflight is green) -->
<div class="settings-card" id="warn-card" style="display:none">
    <h3>2. Fontos tudnivalók</h3>
    <div class="alert alert-warning" style="margin-bottom:.75rem">
        A helyreállítási kód a mentései utolsó kulcsa. Pontosan egyszer jelenik meg — a rendszer
        sehol nem tárolja, és a Felhom sem ismeri. Ha a szerver megsemmisül, a távoli mentések CSAK
        ezzel a kóddal állíthatók vissza. Írja fel papírra vagy mentse jelszókezelőbe — de NE ezen a
        szerveren tárolja.
    </div>
    {{if .Receremony}}
    <div class="alert alert-info" style="margin-bottom:.75rem">
        Az új kód létrehozásával a korábbi kód a jövőbeli mentésekre érvényét veszti; a már meglévő
        mentési előzményekhez érvényes marad.
    </div>
    {{end}}
    <h3 style="margin-top:1.25rem">3. Megerősítés</h3>
    <form id="start-form" onsubmit="return startCeremony(event)">
        <div class="form-group">
            <label for="reauth-password">A folytatáshoz adja meg a bejelentkezési jelszavát</label>
            <input type="password" id="reauth-password" class="form-control" autocomplete="current-password" required style="max-width:280px">
        </div>
        <div id="start-error" class="alert alert-error" style="display:none;margin-bottom:.75rem"></div>
        <div class="form-actions">
            <button type="submit" class="btn btn-primary" id="start-btn">Kód létrehozása</button>
            <a href="/backups/remote" class="btn btn-outline">Mégsem</a>
        </div>
    </form>
</div>

<!-- 4. Progress -->
<div class="settings-card" id="progress-card" style="display:none">
    <h3>4. Létrehozás</h3>
    <p class="form-hint" id="progress-line">A kód létrehozása folyamatban… (általában néhány másodperc)</p>
    <div id="progress-error" class="alert alert-error" style="display:none"></div>
</div>

<!-- 5. Reveal + 6. Typed-back + 7. Finish -->
<div class="settings-card" id="reveal-card" style="display:none">
    <h3>5. A helyreállítási kód</h3>
    <div id="reveal-gate">
        <p class="form-hint">A kód pontosan egyszer jeleníthető meg. Készítsen elő papírt és tollat, mielőtt megnyomja a gombot.</p>
        <button class="btn btn-primary" id="reveal-btn" onclick="claimCode()">Kód megjelenítése</button>
        <div id="reveal-error" class="alert alert-error" style="display:none;margin-top:.75rem"></div>
    </div>
    <div id="reveal-shown" style="display:none">
        <div class="alert alert-warning" style="margin-bottom:.5rem">Ez mostantól az élő helyreállítási kód — a korábbi kód érvényét vesztette. Mentse el most: a kód többé nem jeleníthető meg.</div>
        <pre class="mono" id="code-block" style="white-space:pre-wrap;word-break:break-all;background:var(--bg-0);border:1px solid var(--line);border-radius:var(--radius);padding:1rem;font-size:1.05rem"></pre>
        <button type="button" class="btn btn-ghost btn-sm" id="code-blur-toggle" onclick="toggleCodeBlur()">Elrejtés</button>
        <h3 style="margin-top:1.25rem">6. Ellenőrzés</h3>
        <div class="form-group">
            <label id="verify-label" for="verify-w1">Megerősítés: írja be a kód szavait</label>
            <div style="display:flex;gap:.5rem">
                <input type="text" id="verify-w1" class="form-control" autocomplete="off" style="max-width:180px">
                <input type="text" id="verify-w2" class="form-control" autocomplete="off" style="max-width:180px">
            </div>
            <div id="verify-error" class="alert alert-error" style="display:none;margin-top:.5rem">A megadott szavak nem egyeznek — ellenőrizze a felírt kódot.</div>
        </div>
        <button class="btn btn-sm" id="verify-btn" onclick="verifyWords()">Szavak ellenőrzése</button>
        <div id="finish-block" style="display:none;margin-top:1rem">
            <h3>7. Befejezés</h3>
            {{/* v0.138.0: the "what happens next" note — the wizard finishes to /backups/remote,
                 where the interim "awaiting hub confirmation" card shows until the flip. Setting
                 expectations here avoids surprise at that card. */}}
            <div class="alert alert-info" style="margin-bottom:.75rem">
                <strong>Mi történik ezután?</strong> A helyreállítási kód elkészült, és a rendszer elküldi a központba megerősítésre — ez általában néhány másodperc, legfeljebb 15 perc. Addig a Távoli mentés oldalon a „megerősítésre vár" üzenet látható; a megerősítés után indítható az első távoli mentés.
            </div>
            <label class="toggle" style="margin-bottom:.75rem">
                <input type="checkbox" id="finish-check" onchange="document.getElementById('finish-btn').disabled=!this.checked">
                <span class="toggle-label">Felírtam a kódot, és biztonságos helyen — nem ezen a szerveren — tárolom.</span>
            </label>
            <div><button class="btn btn-primary" id="finish-btn" disabled onclick="finishWizard()">Befejezés</button></div>
        </div>
    </div>
</div>

<!-- Void / unclaimed state -->
<div class="settings-card" id="void-card" style="display:none">
    <div class="alert alert-warning">A kód létrejött, de nem lett megjelenítve — biztonsági okból újra nem kérhető le.
        Indítsa újra a folyamatot: az új kód a régit érvényteleníti.</div>
    <a href="/backup/escrow" class="btn btn-sm btn-primary">Újraindítás</a>
</div>

<script>
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c];}); }

// Hungarian labels for the agent preflight row ids.
var PF_LABELS = {
  pbs_storage_id: 'Mentési tároló (PBS) beállítva',
  dr_tier:        'Helyreállítási (DR) szint aktív',
  age_binary:     'Titkosító eszköz telepítve',
  hub_upload:     'Központi feltöltés beállítva',
  staged_secret:  'Távoli mentés jelszava előkészítve',
  sudo_grant:     'Rendszerjogosultság engedélyezve'
};

// Hungarian DETAIL mapping (v0.127.1): the agent's `detail` strings are operator-English by
// design — presentation is the controller's job. OK rows keep only VALUE-bearing details
// (storage id / binary path); boolean-OK rows render no detail at all. Not-OK rows get the
// Hungarian explanation; unknown/future ids fall back to the raw detail (never blank a failure).
var PF_FAIL_DETAILS = {
  pbs_storage_id: 'nincs beállítva mentési tároló',
  age_binary:     'a titkosító eszköz (age) nem található',
  dr_tier:        'a DR-szint nincs alkalmazva ezen a gépen',
  hub_upload:     'a központi feltöltés nincs beállítva',
  sudo_grant:     'a rendszerjogosultság hiányzik — az ügynök frissítése szükséges',
  staged_secret:  'nincs előkészített jelszó — a varázsló indításkor automatikusan előkészíti, ha a távoli mentés be van állítva'
};
function pfDetail(it){
  if(it.ok){
    if(it.id==='pbs_storage_id' || it.id==='age_binary'){ return it.detail||''; } // the VALUE
    if(it.id==='staged_secret'){ return 'előkészítve'; }
    return ''; // dr_tier / hub_upload / sudo_grant: the check mark + label is complete
  }
  return PF_FAIL_DETAILS[it.id] || (it.detail||'');
}

async function loadPreflight(){
  try{
    var r = await fetch('/api/escrow/preflight'); var j = await r.json();
    if(!j.ok && !j.data){ throw new Error(j.error||'Hiba'); }
    var d = j.data||{};
    var html='<ul style="list-style:none;padding:0;margin:0">';
    (d.items||[]).forEach(function(it){
      // staged_secret is informational: the controller re-stages before every run when offsite is
      // configured, so a red row there would be noise — render it neutral when not ok.
      var informational = it.id==='staged_secret';
      var mark = it.ok ? '<span style="color:var(--blue-bright)">&#10003;</span>'
               : (informational ? '<span style="color:var(--text-3)">&#8226;</span>'
                                : '<span style="color:var(--crit)">&#10007;</span>');
      var det = pfDetail(it);
      var line = mark+' '+esc(PF_LABELS[it.id]||it.id);
      if(det){ line += ' <span class="form-hint">— '+esc(det)+'</span>'; }
      // On failure keep the agent's raw detail as a muted diagnostic (English acceptable there) —
      // unless the fallback above already IS the raw detail (unknown id).
      if(!it.ok && it.detail && det!==it.detail){ line += ' <span class="form-hint" style="color:var(--text-3)">('+esc(it.detail)+')</span>'; }
      html+='<li style="padding:.35rem 0;border-bottom:1px solid var(--line-soft)">'+line+'</li>';
    });
    if(!d.agent_supported){
      html+='<li style="padding:.35rem 0"><span style="color:var(--crit)">&#10007;</span> A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik.</li>';
    }
    html+='</ul>';
    document.getElementById('pf-list').innerHTML=html;
    if(d.ok){
      document.getElementById('warn-card').style.display='block';
    } else {
      document.getElementById('pf-list').insertAdjacentHTML('beforeend',
        '<p class="form-hint" style="margin-top:.75rem">A folyamat a piros feltételek teljesüléséig nem indítható.</p>');
    }
  }catch(e){
    var el=document.getElementById('pf-error'); el.style.display='block';
    el.textContent='Az előfeltételek ellenőrzése nem sikerült: '+e.message;
  }
}

async function startCeremony(ev){
  ev.preventDefault();
  var btn=document.getElementById('start-btn'); btn.disabled=true;
  var errEl=document.getElementById('start-error'); errEl.style.display='none';
  try{
    var body=new URLSearchParams({password:document.getElementById('reauth-password').value});
    var r=await fetch('/api/escrow/start',{method:'POST',
      headers:Object.assign({'Content-Type':'application/x-www-form-urlencoded'},csrfHeaders()),body:body});
    var j=await r.json();
    if(!j.ok){ errEl.textContent=j.error||'A folyamat indítása nem sikerült.'; errEl.style.display='block'; btn.disabled=false; return false; }
    document.getElementById('reauth-password').value='';
    document.getElementById('warn-card').style.display='none';
    document.getElementById('progress-card').style.display='block';
    pollStatus();
  }catch(e){ errEl.textContent='Hiba: '+e.message; errEl.style.display='block'; btn.disabled=false; }
  return false;
}

var pollTimer=null;
function pollStatus(){
  pollTimer=setInterval(async function(){
    try{
      var r=await fetch('/api/escrow/status'); var j=await r.json();
      var d=(j&&j.data)||{};
      if(d.phase==='done'){
        clearInterval(pollTimer);
        document.getElementById('progress-card').style.display='none';
        if(d.claimable){ document.getElementById('reveal-card').style.display='block'; }
        else { document.getElementById('void-card').style.display='block'; }
      } else if(d.phase==='failed'){
        clearInterval(pollTimer);
        var el=document.getElementById('progress-error'); el.style.display='block';
        el.textContent='A létrehozás nem sikerült.'+(d.detail?(' Részletek: '+d.detail):'');
      } else if(d.phase==='unclaimed_void' || d.phase==='none'){
        clearInterval(pollTimer);
        document.getElementById('progress-card').style.display='none';
        document.getElementById('void-card').style.display='block';
      }
    }catch(e){ /* transient poll error — keep polling */ }
  }, 2000);
}

// The claimed code lives ONLY in this closure variable + the #code-block textContent, until
// finishWizard() clears both. It never leaves the page's JS scope.
var claimedR='';
var verifyIdx=[0,0];

async function claimCode(){
  var btn=document.getElementById('reveal-btn'); btn.disabled=true;
  var errEl=document.getElementById('reveal-error'); errEl.style.display='none';
  try{
    var r=await fetch('/api/escrow/claim',{method:'POST',headers:csrfHeaders()});
    var j=await r.json();
    if(r.status===410){ document.getElementById('reveal-card').style.display='none'; document.getElementById('void-card').style.display='block'; return; }
    if(!j.ok || !j.data || !j.data.recovery_code){ errEl.textContent=j.error||'A kód lekérése nem sikerült.'; errEl.style.display='block'; btn.disabled=false; return; }
    claimedR=j.data.recovery_code;
    // Typed-back positions FIRST (v0.127.1: the rendering below highlights them): two random
    // distinct 1-based word positions of the dash-separated code.
    var words=claimedR.split('-');
    var n=words.length;
    var a=1+Math.floor(Math.random()*n), b=1+Math.floor(Math.random()*n);
    while(b===a){ b=1+Math.floor(Math.random()*n); }
    verifyIdx=[Math.min(a,b),Math.max(a,b)];
    // Span-per-word DOM build — createElement + textContent + createTextNode ONLY. R never flows
    // through innerHTML/insertAdjacentHTML: with no HTML parse context there is no injection
    // surface, the same guarantee the old single textContent assignment gave. The two words the
    // customer must type back get the warn highlight so they can be found on paper too.
    var block=document.getElementById('code-block');
    block.textContent='';
    words.forEach(function(w,i){
      if(i>0){ block.appendChild(document.createTextNode('-')); }
      var sp=document.createElement('span');
      sp.textContent=w;
      if(i+1===verifyIdx[0] || i+1===verifyIdx[1]){ sp.style.color='var(--warn)'; sp.style.fontWeight='600'; }
      block.appendChild(sp);
    });
    document.getElementById('reveal-gate').style.display='none';
    document.getElementById('reveal-shown').style.display='block';
    document.getElementById('verify-label').textContent='Megerősítés: írja be a kód '+verifyIdx[0]+'. és '+verifyIdx[1]+'. szavát';
  }catch(e){ errEl.textContent='Hiba: '+e.message; errEl.style.display='block'; btn.disabled=false; }
}

// Manual hide toggle (v0.127.2 — Viktor's live finding reversed the v0.127.1 auto-blur: blurring
// the code the moment a verification input gets focus made typing the words HARDER, since most
// customers type from the screen. The code now stays visible; "Elrejtés" is offered from the
// reveal onward for when hiding is wanted, e.g. a screen share). R stays in the JS closure;
// the blur is pure presentation.
var codeBlurred=false;
function setCodeBlur(on){
  codeBlurred=on;
  document.getElementById('code-block').style.filter=on?'blur(6px)':'';
  document.getElementById('code-blur-toggle').textContent=on?'Megjelenítés':'Elrejtés';
}
function toggleCodeBlur(){ setCodeBlur(!codeBlurred); }

function verifyWords(){
  var words=claimedR.split('-');
  var w1=(document.getElementById('verify-w1').value||'').trim().toLowerCase();
  var w2=(document.getElementById('verify-w2').value||'').trim().toLowerCase();
  var ok = w1===words[verifyIdx[0]-1] && w2===words[verifyIdx[1]-1];
  document.getElementById('verify-error').style.display=ok?'none':'block';
  if(ok){ document.getElementById('finish-block').style.display='block'; }
}

function finishWizard(){
  // Drop every copy of the code this page holds, then leave. (Assigning textContent replaces ALL
  // child nodes with one empty text node — the per-word spans are removed with it, verified.)
  claimedR='';
  document.getElementById('code-block').textContent='';
  window.location='/backups/remote';
}

loadPreflight();
</script>

{{end}}
{{template "layout_end" .}}
{{end}}
