(() => {
  'use strict';
  const STORAGE_KEY='apu_network_subnet_history_v37';
  const $=(s,r=document)=>r.querySelector(s), $$=(s,r=document)=>[...r.querySelectorAll(s)];
  const esc=v=>String(v??'').replace(/[&<>'"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[c]));
  const uid=()=>`NET-${Date.now().toString(36).toUpperCase()}${Math.random().toString(36).slice(2,5).toUpperCase()}`;
  const clamp=(v,min,max)=>Math.min(max,Math.max(min,Number(v)||0));
  const u32=n=>Number(BigInt.asUintN(32,BigInt(n)));
  function parseIp(value){const p=String(value||'').trim().split('.');if(p.length!==4)return null;const nums=p.map(x=>Number(x));if(nums.some((n,i)=>!/^\d{1,3}$/.test(p[i])||n<0||n>255))return null;return (((nums[0]<<24)>>>0)+(nums[1]<<16)+(nums[2]<<8)+nums[3])>>>0;}
  function ip(n){n=Number(n)>>>0;return [n>>>24,(n>>>16)&255,(n>>>8)&255,n&255].join('.');}
  function binary(n){return [n>>>24,(n>>>16)&255,(n>>>8)&255,n&255].map(x=>x.toString(2).padStart(8,'0')).join('.');}
  function maskFromPrefix(prefix){prefix=clamp(prefix,0,32);if(prefix===0)return 0;return (0xffffffff << (32-prefix))>>>0;}
  function wildcard(mask){return (~mask)>>>0;}
  function parseCidr(value,prefixFallback=24){const parts=String(value||'').trim().split('/');const addr=parseIp(parts[0]);const prefix=parts.length>1?Number(parts[1]):Number(prefixFallback);if(addr===null||!Number.isInteger(prefix)||prefix<0||prefix>32)return null;return{addr,prefix};}
  function scope(addr){const a=addr>>>24,b=(addr>>>16)&255;if(a===10||(a===172&&b>=16&&b<=31)||(a===192&&b===168))return'Private';if(a===127)return'Loopback';if(a===169&&b===254)return'Link-local';if(a>=224&&a<=239)return'Multicast';if(a===0)return'Reserved';return'Public';}
  function ipClass(addr){const a=addr>>>24;if(a<128)return'A';if(a<192)return'B';if(a<224)return'C';if(a<240)return'D';return'E';}
  function calculate(value,prefixFallback){const parsed=parseCidr(value,prefixFallback);if(!parsed)throw new Error('Alamat IPv4 atau prefix tidak valid.');const {addr,prefix}=parsed,mask=maskFromPrefix(prefix),network=(addr&mask)>>>0,broadcast=(network|wildcard(mask))>>>0,total=2**(32-prefix);let first,last,usable;if(prefix===32){first=last=network;usable=1;}else if(prefix===31){first=network;last=broadcast;usable=2;}else{first=(network+1)>>>0;last=(broadcast-1)>>>0;usable=Math.max(0,total-2);}return{input:ip(addr),prefix,cidr:`${ip(network)}/${prefix}`,mask:ip(mask),wildcard:ip(wildcard(mask)),network:ip(network),broadcast:ip(broadcast),first:ip(first),last:ip(last),total,usable,scope:scope(addr),className:ipClass(addr),binaryIp:binary(addr),binaryMask:binary(mask),networkInt:network,broadcastInt:broadcast};}
  function prefixForHosts(hosts){hosts=Math.max(1,Math.floor(Number(hosts)||1));for(let bits=0;bits<=32;bits++){const usable=bits===0?1:bits===1?2:Math.max(0,2**bits-2);if(usable>=hosts)return 32-bits;}return 0;}
  function formatNumber(n){return new Intl.NumberFormat('id-ID').format(n);}
  function loadHistory(){try{const d=JSON.parse(localStorage.getItem(STORAGE_KEY));return Array.isArray(d)?d:[];}catch(_){return[];}}
  let history=loadHistory(),lastResult=null;
  const save=()=>localStorage.setItem(STORAGE_KEY,JSON.stringify(history.slice(0,100)));
  function toast(m){const e=$('#apuNetworkToast');if(!e)return;e.textContent=m;e.classList.add('is-visible');clearTimeout(e._t);e._t=setTimeout(()=>e.classList.remove('is-visible'),1900);}
  function download(name,content,type){const b=new Blob([content],{type}),u=URL.createObjectURL(b),a=document.createElement('a');a.href=u;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(u),500);}
  function resultCards(r){return [['CIDR',r.cidr],['Subnet mask',r.mask],['Wildcard',r.wildcard],['Network',r.network],['Broadcast',r.broadcast],['Usable range',`${r.first} – ${r.last}`],['Usable hosts',formatNumber(r.usable)],['Address scope',`${r.scope} · Class ${r.className}`]].map(([a,b])=>`<div class="apu-result"><small>${esc(a)}</small><strong>${esc(b)}</strong></div>`).join('');}
  function renderHistory(){const body=$('#netHistoryBody');if(!body)return;body.innerHTML=history.length?history.map(r=>`<tr><td><code>${esc(r.id)}</code><br><small>${new Date(r.createdAt).toLocaleString('id-ID')}</small></td><td>${esc(r.input)}/${r.prefix}</td><td>${esc(r.network)}</td><td>${esc(r.broadcast)}</td><td>${formatNumber(r.usable)}</td><td class="apu-row-actions"><button class="apu-btn net-load" data-id="${esc(r.id)}">Muat</button><button class="apu-btn apu-btn--danger net-delete" data-id="${esc(r.id)}">Hapus</button></td></tr>`).join(''):'<tr><td colspan="6" class="apu-empty">Belum ada riwayat tersimpan.</td></tr>';}
  function calculateMain(){try{const r=calculate($('#netIp').value,$('#netPrefix').value);lastResult=r;$('#netPrefix').value=r.prefix;$('#netResults').innerHTML=resultCards(r);$('#netBinary').innerHTML=`<div class="apu-diagnostic"><strong>Representasi biner</strong><p><code>IP&nbsp;&nbsp;&nbsp; ${esc(r.binaryIp)}</code></p><p><code>MASK&nbsp; ${esc(r.binaryMask)}</code></p></div>`;toast('Perhitungan subnet selesai.');}catch(e){lastResult=null;$('#netResults').innerHTML='';$('#netBinary').innerHTML=`<div class="apu-diagnostic"><strong>Input belum valid</strong><p>${esc(e.message)}</p></div>`;toast(e.message);}}
  function saveCurrent(){if(!lastResult){toast('Hitung subnet terlebih dahulu.');return;}const rec={...lastResult,id:uid(),createdAt:new Date().toISOString()};history.unshift(rec);save();renderHistory();toast('Hasil disimpan ke riwayat lokal.');}
  function calculateVlsm(){const base=parseCidr($('#vlsmBase').value,24);if(!base){$('#vlsmBody').innerHTML='<tr><td colspan="7" class="apu-empty">Base network tidak valid.</td></tr>';return;}const baseCalc=calculate(`${ip(base.addr)}/${base.prefix}`),requests=($('#vlsmHosts').value.match(/\d+/g)||[]).map(Number).filter(n=>n>0).sort((a,b)=>b-a);if(!requests.length){$('#vlsmBody').innerHTML='<tr><td colspan="7" class="apu-empty">Masukkan kebutuhan host, misalnya 120, 60, 20, 10.</td></tr>';return;}let cursor=baseCalc.networkInt;const end=baseCalc.broadcastInt;const rows=[];for(let i=0;i<requests.length;i++){const hosts=requests[i],prefix=prefixForHosts(hosts),block=2**(32-prefix);cursor=Math.ceil(cursor/block)*block;if(cursor+block-1>end){rows.push({error:true,index:i+1,hosts});continue;}const r=calculate(`${ip(cursor)}/${prefix}`);rows.push({index:i+1,hosts,...r});cursor+=block;}$('#vlsmBody').innerHTML=rows.map(r=>r.error?`<tr><td>${r.index}</td><td>${r.hosts}</td><td colspan="5"><span class="apu-badge apu-badge--negative">Tidak muat pada base network</span></td></tr>`:`<tr><td>${r.index}</td><td>${formatNumber(r.hosts)}</td><td><code>${r.cidr}</code></td><td>${r.mask}</td><td>${r.first}</td><td>${r.last}</td><td>${formatNumber(r.usable)}</td></tr>`).join('');}
  function troubleshoot(){const local=parseCidr($('#diagLocal').value,$('#diagPrefix').value),gateway=parseIp($('#diagGateway').value),target=parseIp($('#diagTarget').value),dns=$('#diagDns').value.trim();const items=[];if(!local)items.push('Alamat IP lokal atau prefix tidak valid.');if(gateway===null)items.push('Default gateway tidak valid.');if(target===null)items.push('Target tidak valid.');if(local&&gateway!==null){const mask=maskFromPrefix(local.prefix);if(((local.addr&mask)>>>0)!==((gateway&mask)>>>0))items.push('Gateway berada di luar subnet lokal; periksa IP, prefix, atau gateway.');else items.push('Gateway berada pada subnet lokal yang sama.');}if(local&&target!==null){const mask=maskFromPrefix(local.prefix);const same=((local.addr&mask)>>>0)===((target&mask)>>>0);items.push(same?'Target berada pada subnet lokal; ARP dan switching harus diperiksa.':'Target berada di jaringan berbeda; trafik akan melewati gateway.');}if(dns){items.push(parseIp(dns)!==null?'DNS diisi sebagai alamat IPv4 valid.':'DNS bukan alamat IPv4; nama host hanya dapat diuji setelah resolver aktif.');}const cmds=[];if(local)cmds.push(`ping ${gateway!==null?ip(gateway):'GATEWAY'}`);if(target!==null)cmds.push(`ping ${ip(target)}`,`tracert ${ip(target)}  (Windows)`,`traceroute ${ip(target)}  (Linux/macOS)`);cmds.push('ipconfig /all  (Windows)','ip addr && ip route  (Linux)','nslookup example.com');$('#diagOutput').innerHTML=`<div class="apu-diagnostic"><strong>Hasil diagnosis</strong><ul>${items.map(x=>`<li>${esc(x)}</li>`).join('')}</ul><h4>Perintah yang disarankan</h4><pre><code>${esc(cmds.join('\n'))}</code></pre></div>`;}
  function mount(){if($('#apuNetworkStudio'))return;const host=document.createElement('section');host.id='apuNetworkStudio';host.className='apu-data-studio';host.innerHTML=`<div class="apu-studio-shell"><div class="apu-studio-hero"><div><span class="apu-studio-kicker">Network Subnet · calculation workspace</span><h2>Subnet Intelligence & Troubleshooter</h2><p>Hitung IPv4/CIDR, tampilkan network dan host range, susun VLSM, diagnosis konfigurasi, serta simpan dan ekspor riwayat perhitungan.</p></div><div class="apu-actions"><button class="apu-btn" id="netExport">Ekspor riwayat JSON</button><button class="apu-btn apu-btn--danger" id="netClear">Hapus riwayat</button></div></div><div class="apu-studio-tabs" role="tablist"><button class="apu-tab" aria-selected="true" data-tab="subnet">Subnet Calculator</button><button class="apu-tab" aria-selected="false" data-tab="vlsm">VLSM Planner</button><button class="apu-tab" aria-selected="false" data-tab="diagnose">Troubleshooter</button></div>
    <div class="apu-tab-panel" data-panel="subnet"><div class="apu-studio-grid"><article class="apu-studio-card"><h3>IPv4 / CIDR calculator</h3><div class="apu-studio-form"><div class="apu-studio-form-grid"><div class="apu-field apu-field--wide"><label for="netIp">Alamat IPv4 atau CIDR</label><input id="netIp" value="192.168.10.25/24" placeholder="192.168.10.25/24"></div><div class="apu-field"><label for="netPrefix">Prefix</label><input id="netPrefix" type="number" min="0" max="32" value="24"></div></div><div class="apu-actions"><button class="apu-btn apu-btn--primary" id="netCalculate">Hitung subnet</button><button class="apu-btn" id="netSave">Simpan hasil</button></div></div><div id="netResults" class="apu-result-grid"></div><div id="netBinary"></div></article><article class="apu-studio-card"><h3>Riwayat perhitungan</h3><div class="apu-table-wrap"><table class="apu-table"><thead><tr><th>ID / waktu</th><th>Input</th><th>Network</th><th>Broadcast</th><th>Hosts</th><th>Operasi</th></tr></thead><tbody id="netHistoryBody"></tbody></table></div></article></div></div>
    <div class="apu-tab-panel" data-panel="vlsm" hidden><article class="apu-studio-card"><h3>VLSM allocation planner</h3><div class="apu-studio-form-grid"><div class="apu-field"><label for="vlsmBase">Base network</label><input id="vlsmBase" value="10.10.0.0/22"></div><div class="apu-field"><label for="vlsmHosts">Kebutuhan host</label><input id="vlsmHosts" value="120, 60, 20, 10" placeholder="120, 60, 20, 10"></div></div><div class="apu-actions" style="margin:14px 0"><button class="apu-btn apu-btn--primary" id="vlsmCalculate">Susun VLSM</button></div><div class="apu-table-wrap"><table class="apu-table"><thead><tr><th>#</th><th>Host diminta</th><th>Network / prefix</th><th>Mask</th><th>First host</th><th>Last host</th><th>Kapasitas</th></tr></thead><tbody id="vlsmBody"></tbody></table></div></article></div>
    <div class="apu-tab-panel" data-panel="diagnose" hidden><div class="apu-studio-grid"><article class="apu-studio-card"><h3>Configuration troubleshooter</h3><div class="apu-studio-form-grid"><div class="apu-field"><label for="diagLocal">IP lokal</label><input id="diagLocal" value="192.168.10.25"></div><div class="apu-field"><label for="diagPrefix">Prefix</label><input id="diagPrefix" type="number" min="0" max="32" value="24"></div><div class="apu-field"><label for="diagGateway">Default gateway</label><input id="diagGateway" value="192.168.10.1"></div><div class="apu-field"><label for="diagTarget">Target</label><input id="diagTarget" value="8.8.8.8"></div><div class="apu-field apu-field--wide"><label for="diagDns">DNS atau resolver</label><input id="diagDns" value="1.1.1.1"></div></div><div class="apu-actions" style="margin-top:14px"><button class="apu-btn apu-btn--primary" id="diagRun">Jalankan diagnosis</button></div></article><article class="apu-studio-card"><h3>Diagnosis dan perintah</h3><div id="diagOutput" class="apu-diagnostic"><p>Masukkan konfigurasi lalu jalankan diagnosis.</p></div></article></div></div></div><div class="apu-toast" id="apuNetworkToast" role="status"></div>`;document.body.appendChild(host);const jump=document.createElement('button');jump.className='apu-studio-jump';jump.type='button';jump.textContent='Subnet Tools ↓';jump.addEventListener('click',()=>host.scrollIntoView({behavior:'smooth'}));document.body.appendChild(jump);
    $$('.apu-tab',host).forEach(btn=>btn.addEventListener('click',()=>{$$('.apu-tab',host).forEach(b=>b.setAttribute('aria-selected',String(b===btn)));$$('.apu-tab-panel',host).forEach(p=>p.hidden=p.dataset.panel!==btn.dataset.tab);}));$('#netCalculate').addEventListener('click',calculateMain);$('#netSave').addEventListener('click',saveCurrent);$('#vlsmCalculate').addEventListener('click',calculateVlsm);$('#diagRun').addEventListener('click',troubleshoot);$('#netHistoryBody').addEventListener('click',e=>{const l=e.target.closest('.net-load'),d=e.target.closest('.net-delete');if(l){const r=history.find(x=>x.id===l.dataset.id);if(r){$('#netIp').value=`${r.input}/${r.prefix}`;$('#netPrefix').value=r.prefix;calculateMain();host.scrollIntoView({behavior:'smooth'});}}if(d){history=history.filter(x=>x.id!==d.dataset.id);save();renderHistory();toast('Riwayat dihapus.');}});$('#netClear').addEventListener('click',async()=>{if(await APUDialog.confirm('Hapus seluruh riwayat subnet lokal?',{intent:'danger'})){history=[];save();renderHistory();toast('Riwayat dikosongkan.');}});$('#netExport').addEventListener('click',()=>download('network-subnet-history.json',JSON.stringify(history,null,2),'application/json'));renderHistory();calculateMain();calculateVlsm();
  }
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',()=>setTimeout(mount,450),{once:true});else setTimeout(mount,450);
})();
