const $ = id => document.getElementById(id);
let statusData = null;
let activeRunId = '';
let executionRunId = '';
let executing = false;
let worker = null;
const pct = (a,b) => b ? Math.max(0,Math.min(100,100*a/b)) : 0;

async function api(path, options={}){
  const response = await fetch(path, options);
  const text = await response.text();
  let data;
  try { data = text ? JSON.parse(text) : {}; } catch { data = {detail:text}; }
  if(!response.ok) throw new Error(data.detail || text || `HTTP ${response.status}`);
  return data;
}
function setTag(node, kind, text){ node.className=`tag ${kind||''}`; node.textContent=text; }

const modeNotes = {
  LEAVE_ONE_RADIUS_OUT: 'Branch L. Exhaustive deletion for n≤12; five fixed radial ranks for n>12. Registered count: 782.',
  THINNING_N10_DETERMINISTIC: 'Branch T. Exactly 10 evenly spaced observed radial ranks per galaxy. Random thinning is separate and locked. Registered count: 121.',
  COVERAGE: 'Branch C. Paired FULL8 and INNER85_8 profiles at fixed n=8. Registered count: 242.',
  ML_SENSITIVITY: 'Branch M. Disk 0.4/0.6 for all galaxies and bulge 0.6/0.8 only where a bulge is present. Registered count: 302.'
};
function updateModeNote(){ $('modeNote').textContent = modeNotes[$('mode').value] || ''; }
$('mode').onchange = updateModeNote;

async function loadStatus(){
  statusData = await api('/api/status');
  $('status').textContent = `GAL-FINGERPRINT-I ${statusData.version} · protocol ${statusData.protocol_version} · GATE_C`;
  $('installState').textContent = statusData.installed_in_project ? 'INSTALLED IN PROJECT' : 'STANDALONE REGISTERED PACKAGE';
  $('exportRoot').textContent = statusData.export_root;
  $('runRoot').textContent = statusData.run_root;
  const gb = statusData.gate_b || {};
  $('gateBState').textContent = gb.gate_b_pass
    ? `Gate B prerequisite verified · ${gb.verdict} · runtime ${gb.successful_runtime_package_version} · legacy export label ${gb.legacy_exported_controller_label}`
    : `Gate B prerequisite unavailable · ${gb.error || 'evidence check failed'}`;
  setTag($('gateTag'), gb.gate_b_pass ? 'done' : 'fail', gb.gate_b_pass ? 'GATE C AUTHORIZED' : 'GATE B REQUIRED');
  updateModeNote();
  renderRuns(statusData.runs || []);
}

$('validate').onclick = async () => {
  $('validation').textContent = 'Validating frozen corpus, Gate B evidence, controller provenance, branch counts, thinning split, and runtime writes…';
  try {
    const data = await api('/api/validate',{method:'POST'});
    $('validation').textContent = JSON.stringify(data,null,2);
    setTag($('gateTag'),data.validation_pass?'done':'fail',data.validation_pass?'VALIDATED / GATE C':'VALIDATION FAILURE');
  } catch(error){ $('validation').textContent = `VALIDATION FAILED · ${error.message}`; }
};

$('createRun').onclick = async () => {
  try {
    const data = await api('/api/runs', {
      method:'POST', headers:{'Content-Type':'application/json'},
      body:JSON.stringify({mode:$('mode').value,execution_scope:'REGISTERED_EXECUTION'})
    });
    activeRunId = data.run_id;
    $('message').textContent = `Created or selected ${activeRunId}`;
    await refreshRuns(activeRunId);
  } catch(error){ $('message').textContent = `CREATE FAILED · ${error.message}`; }
};

function renderRuns(runs, selected=''){
  const select = $('runSelect');
  const old = selected || activeRunId || select.value;
  select.innerHTML = '<option value="">No run selected</option>';
  const tbody = $('runTable'); tbody.innerHTML = '';
  for (const run of runs){
    const option = document.createElement('option');
    option.value = run.run_id;
    option.textContent = `${run.run_id} · ${run.mode} · ${run.state} · ${run.completed}/${run.total}`;
    select.appendChild(option);
    const tr = document.createElement('tr');
    const values = [run.run_id,run.mode,run.execution_scope,run.state,`${run.completed}/${run.total}`,run.updated_at||''];
    values.forEach((value,index)=>{ const td=document.createElement('td'); td.textContent=value; if(index===3) td.className=`state-${run.state}`; tr.appendChild(td); });
    tr.onclick=()=>{ select.value=run.run_id; activeRunId=run.run_id; showRun(run.run_id); };
    tbody.appendChild(tr);
  }
  if ([...select.options].some(o=>o.value===old)){ select.value=old; activeRunId=old; showRun(old); }
}
async function refreshRuns(selected=''){ renderRuns(await api('/api/runs'),selected); }
$('refreshRuns').onclick=()=>refreshRuns();
$('runSelect').onchange=()=>{ activeRunId=$('runSelect').value; if(activeRunId) showRun(activeRunId); };

async function showRun(runId){
  if(!runId) return;
  const run = await api(`/api/runs/${encodeURIComponent(runId)}`);
  $('counter').textContent = `${run.completed} / ${run.total} profiles`;
  $('overallBar').style.width = `${pct(run.completed,run.total)}%`;
  $('message').textContent = `${run.state} · ${run.message}`;
  setTag($('runTag'), run.state==='COMPLETE'?'done':run.state==='RUNNING'?'running':'', run.state);
  if(run.state==='COMPLETE'){
    $('resultPanel').classList.remove('hidden');
    $('resultPaths').textContent=JSON.stringify(run.export_paths,null,2);
    $('download').href=`/api/runs/${encodeURIComponent(runId)}/download`;
  }
}

$('startRun').onclick = async () => {
  const selectedRunId = $('runSelect').value;
  if(!selectedRunId){ $('message').textContent='Select or create a run first.'; return; }
  activeRunId = selectedRunId;
  executionRunId = selectedRunId;
  executing=true;
  setExecutionLock(true);
  setTag($('runTag'),'running','RUNNING');
  await executeNext(executionRunId);
};
$('pauseRun').onclick = () => {
  executing=false; executionRunId='';
  if(worker){ worker.terminate(); worker=null; }
  setExecutionLock(false);
  $('currentBar').style.width='0%';
  $('message').textContent='PAUSED · completed checkpoints retained; the unfinished profile will restart.';
  setTag($('runTag'),'','PAUSED');
};
function setExecutionLock(locked){
  $('startRun').disabled=locked;
  $('pauseRun').disabled=!locked;
  $('runSelect').disabled=locked;
  $('createRun').disabled=locked;
  $('refreshRuns').disabled=locked;
  $('mode').disabled=locked;
}

async function executeNext(runId){
  if(!executing || !runId || runId !== executionRunId) return;
  try{
    const data=await api(`/api/runs/${encodeURIComponent(runId)}/next`,{method:'POST'});
    updateRunDisplay(data.run);
    if(!data.item){
      $('message').textContent='All profiles checkpointed. Finalizing direct exports…';
      const finalRun=await api(`/api/runs/${encodeURIComponent(runId)}/finalize`,{method:'POST'});
      executing=false; executionRunId=''; setExecutionLock(false);
      updateRunDisplay(finalRun);
      $('resultPanel').classList.remove('hidden');
      $('resultPaths').textContent=JSON.stringify(finalRun.export_paths,null,2);
      $('download').href=`/api/runs/${encodeURIComponent(runId)}/download`;
      await refreshRuns(runId); await checkGateC();
      return;
    }
    runWorker(data.item,data.run.settings,runId);
  }catch(error){
    executing=false; executionRunId=''; setExecutionLock(false);
    try{
      const retained=await api(`/api/runs/${encodeURIComponent(runId)}`);
      updateRunDisplay(retained);
      $('message').textContent=`FINALIZATION OR EXECUTION FAILURE · ${error.message} · ${retained.completed}/${retained.total} checkpoints retained`;
      setTag($('runTag'),'fail',retained.state==='AWAITING_FINALIZATION'?'FINALIZATION RETRY READY':'EXECUTION FAILURE');
    }catch{
      $('message').textContent=`EXECUTION FAILURE · ${error.message}`;
      setTag($('runTag'),'fail','EXECUTION FAILURE');
    }
  }
}
function runWorker(item,settings,runId){
  $('currentProfile').textContent=`${item.ladder_name} · n=${item.n_realization}`;
  $('currentBar').style.width='0%';
  worker=new Worker('/static/struc_i_worker.js?v=0.2.2');
  worker.onmessage=async event=>{
    const data=event.data;
    if(data.type==='progress'){
      $('currentBar').style.width=`${Math.round(data.fraction*100)}%`;
      $('message').textContent=`${item.ladder_name} · κ=${Number(data.kappa).toPrecision(4)} · Aκ=${Number(data.Ak).toFixed(4)} · ν=${data.nu}`;
    }else if(data.type==='complete'){
      worker.terminate();worker=null;
      try{
        const accepted=await api(`/api/runs/${encodeURIComponent(runId)}/profiles`,{
          method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data.payload)
        });
        $('counter').textContent=`${accepted.completed} / ${accepted.total} profiles`;
        $('overallBar').style.width=`${pct(accepted.completed,accepted.total)}%`;
        $('currentBar').style.width='100%';
        setTimeout(()=>executeNext(runId),25);
      }catch(error){
        executing=false;executionRunId='';setExecutionLock(false);
        $('message').textContent=`CHECKPOINT OR PROFILE FAILURE · ${error.message}`;
        setTag($('runTag'),'fail','EXECUTION FAILURE');
      }
    }else if(data.type==='error'){
      executing=false;executionRunId='';worker.terminate();worker=null;setExecutionLock(false);
      $('message').textContent=`ENGINE ERROR · ${data.message}`;setTag($('runTag'),'fail','ENGINE ERROR');
    }
  };
  worker.onerror=event=>{ executing=false;executionRunId='';setExecutionLock(false);$('message').textContent=`WORKER ERROR · ${event.message}`; };
  worker.postMessage({item,settings,run_id:runId});
}
function updateRunDisplay(run){
  $('counter').textContent=`${run.completed} / ${run.total} profiles`;
  $('overallBar').style.width=`${pct(run.completed,run.total)}%`;
  $('message').textContent=`${run.state} · ${run.message}`;
  setTag($('runTag'),run.state==='COMPLETE'?'done':run.state==='RUNNING'?'running':'',run.state);
}

async function checkGateC(){
  const data=await api('/api/gate-c/readiness');
  const text=Object.entries(data.branches).map(([mode,v])=>`${mode}: ${v.valid?`COMPLETE ${v.found}/${v.expected}`:`MISSING ${v.found||0}/${v.expected}`}`).join('\n');
  $('gateCReadiness').textContent=`Gate B: ${data.gate_b.gate_b_pass?'PASS':'FAIL'}\n${text}`;
  $('analyzeGateC').disabled=!data.ready;
  setTag($('gateCTag'),data.ready?'done':'',data.ready?'READY':'NOT READY');
  return data;
}
$('checkGateC').onclick=()=>checkGateC().catch(e=>$('gateCReadiness').textContent=e.message);
$('analyzeGateC').onclick=async()=>{
  $('gateCResult').textContent='Computing five-repeat reference centroids, registered source retrieval, geometry-matched impostors, sensitivity distances, neighbour stability, and branch pass checks…';
  try{ const result=await api('/api/gate-c/analyze',{method:'POST'}); $('gateCResult').textContent=JSON.stringify(result,null,2); }
  catch(error){ $('gateCResult').textContent=`ANALYSIS FAILED · ${error.message}`; }
};

(async()=>{ try{await loadStatus();await checkGateC();}catch(error){$('status').textContent=`STARTUP ERROR · ${error.message}`;} })();
