"""
Authorization Manager HTML Template for MCP AI xty.
Simplified UI: Admin generates activation codes, Users input activation codes.
"""

AUTH_MANAGER_TEMPLATE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MCP AI xty - 授权管理器</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
  background:linear-gradient(135deg,#1a1a2e 0%,#16213e 50%,#0f3460 100%);
  min-height:100vh;color:#e0e0e0;
}
::-webkit-scrollbar{width:6px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(123,104,238,0.3);border-radius:3px}
::-webkit-scrollbar-thumb:hover{background:rgba(123,104,238,0.5)}

.app-wrapper{
  max-width:720px;margin:0 auto;padding:32px 20px;min-height:100vh;
  display:flex;flex-direction:column;
}

/* Header */
.page-header{
  display:flex;align-items:center;justify-content:space-between;
  margin-bottom:28px;flex-wrap:wrap;gap:12px;
}
.page-header-left h1{
  font-size:22px;font-weight:700;
  background:linear-gradient(135deg,#7b68ee,#00b4d8);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
}
.page-header-left .data-path{
  font-size:12px;color:#666;margin-top:4px;font-family:monospace;
}
.header-actions{display:flex;gap:8px;align-items:center}
.btn-icon{
  width:36px;height:36px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);
  background:rgba(255,255,255,0.05);color:#aaa;cursor:pointer;
  display:flex;align-items:center;justify-content:center;font-size:16px;
  transition:all 0.2s;
}
.btn-icon:hover{background:rgba(123,104,238,0.2);color:#7b68ee;border-color:rgba(123,104,238,0.3)}

/* Cards */
.card{
  background:rgba(255,255,255,0.05);
  border:1px solid rgba(255,255,255,0.1);
  border-radius:12px;padding:20px;margin-bottom:16px;
  backdrop-filter:blur(10px);
}
.card-title{
  font-size:14px;font-weight:600;color:#7b68ee;margin-bottom:14px;
  display:flex;align-items:center;gap:8px;
}
.card-title .icon{font-size:16px}

/* Instance Info */
.info-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;
}
.info-item{display:flex;flex-direction:column;gap:4px}
.info-label{font-size:11px;color:#888;text-transform:uppercase;letter-spacing:0.5px}
.info-value{font-size:14px;color:#e0e0e0;display:flex;align-items:center;gap:8px}
.info-value.uuid-val{font-family:monospace;font-size:12px;word-break:break-all}
.btn-copy{
  width:24px;height:24px;border:none;background:rgba(123,104,238,0.15);
  color:#7b68ee;border-radius:4px;cursor:pointer;font-size:11px;
  display:flex;align-items:center;justify-content:center;transition:all 0.2s;flex-shrink:0;
}
.btn-copy:hover{background:rgba(123,104,238,0.3)}
.badge{
  display:inline-flex;align-items:center;padding:2px 10px;border-radius:10px;
  font-size:12px;font-weight:500;
}
.badge-admin{background:rgba(123,104,238,0.15);color:#7b68ee}
.badge-user{background:rgba(0,180,216,0.15);color:#00b4d8}
.badge-authorized{background:rgba(76,175,80,0.15);color:#4CAF50}
.badge-unauthorized{background:rgba(244,67,54,0.15);color:#f44336}

/* Form */
.form-group{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.form-label{font-size:12px;color:#888;font-weight:500}
.form-input{
  padding:10px 12px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);
  background:rgba(255,255,255,0.05);color:#e0e0e0;font-size:13px;
  font-family:inherit;outline:none;transition:border-color 0.2s;
}
.form-input:focus{border-color:#7b68ee}
.form-input::placeholder{color:#555}

/* Buttons */
.btn{
  padding:10px 20px;border-radius:8px;border:none;cursor:pointer;
  font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:6px;
  transition:all 0.2s;
}
.btn:disabled{opacity:0.4;cursor:not-allowed}
.btn-primary{background:#7b68ee;color:#fff}
.btn-primary:hover:not(:disabled){background:#6a58d6}
.btn-full{width:100%;justify-content:center}

/* Calendar */
.calendar{
  border:1px solid rgba(255,255,255,0.1);border-radius:10px;
  background:rgba(255,255,255,0.03);overflow:hidden;
}
.calendar-header{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 14px;border-bottom:1px solid rgba(255,255,255,0.06);
}
.calendar-header .month-label{font-size:13px;font-weight:600;color:#ccc}
.calendar-nav{
  width:28px;height:28px;border:none;background:rgba(255,255,255,0.06);
  color:#aaa;border-radius:6px;cursor:pointer;display:flex;align-items:center;
  justify-content:center;font-size:14px;transition:all 0.2s;
}
.calendar-nav:hover{background:rgba(123,104,238,0.2);color:#7b68ee}
.calendar-grid{
  display:grid;grid-template-columns:repeat(7,1fr);padding:6px;gap:2px;
}
.calendar-dow{text-align:center;font-size:10px;color:#666;padding:4px 0;font-weight:600}
.calendar-day{
  text-align:center;padding:6px 0;font-size:12px;border-radius:6px;
  cursor:pointer;transition:all 0.15s;color:#aaa;
}
.calendar-day:hover{background:rgba(123,104,238,0.15);color:#fff}
.calendar-day.other-month{color:#444}
.calendar-day.today{color:#00b4d8;font-weight:600}
.calendar-day.selected{background:#7b68ee;color:#fff;font-weight:600}

/* Time */
.time-selectors{display:flex;gap:8px;align-items:center;margin-top:8px}
.time-selectors label{font-size:12px;color:#888}
.time-select{
  padding:8px 10px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);
  background:rgba(255,255,255,0.05);color:#e0e0e0;font-size:13px;
  font-family:monospace;outline:none;cursor:pointer;appearance:none;
  -webkit-appearance:none;min-width:60px;text-align:center;
}
.time-select:focus{border-color:#7b68ee}
.time-select:disabled{opacity:0.4;cursor:not-allowed}
.time-colon{color:#666;font-size:16px;font-weight:700}

/* Checkbox */
.checkbox-row{
  display:flex;align-items:center;gap:8px;cursor:pointer;padding:6px 0;
}
.checkbox-row input[type="checkbox"]{width:16px;height:16px;accent-color:#7b68ee;cursor:pointer}
.checkbox-row span{font-size:13px;color:#ccc}

/* Result */
.result-area{
  display:none;margin-top:16px;padding:16px;
  background:rgba(76,175,80,0.08);border:1px solid rgba(76,175,80,0.2);
  border-radius:10px;
}
.result-area.show{display:block}
.result-label{font-size:12px;color:#4CAF50;font-weight:600;margin-bottom:8px}
.result-code{
  display:flex;align-items:center;gap:8px;
  background:rgba(0,0,0,0.2);border-radius:8px;padding:12px;
}
.result-code-text{
  flex:1;font-family:monospace;font-size:13px;color:#e0e0e0;
  word-break:break-all;line-height:1.5;user-select:all;
}
.result-info{font-size:11px;color:#888;margin-top:8px;line-height:1.5;white-space:pre-line}

/* Toast */
.toast-container{
  position:fixed;top:20px;right:20px;z-index:2000;display:flex;flex-direction:column;gap:8px;
}
.toast{
  padding:12px 18px;border-radius:10px;font-size:13px;
  display:flex;align-items:center;gap:8px;
  animation:slideInRight 0.3s ease;min-width:280px;
  box-shadow:0 8px 24px rgba(0,0,0,0.3);
}
@keyframes slideInRight{from{transform:translateX(100%);opacity:0}to{transform:translateX(0);opacity:1}}
.toast-success{background:rgba(76,175,80,0.9);color:#fff}
.toast-error{background:rgba(244,67,54,0.9);color:#fff}
.toast-info{background:rgba(123,104,238,0.9);color:#fff}

/* Section visibility */
.admin-section{display:none}
.admin-section.visible{display:block}
.user-section{display:none}
.user-section.visible{display:block}

/* User activation */
.activation-input-row{display:flex;gap:8px;align-items:center}
.activation-input-row input{
  flex:1;padding:10px 12px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);
  background:rgba(255,255,255,0.05);color:#e0e0e0;font-size:13px;
  font-family:monospace;outline:none;
}
.activation-input-row input:focus{border-color:#7b68ee}
.activation-input-row input::placeholder{color:#555}
.activation-status{
  margin-top:8px;padding:8px 12px;border-radius:8px;font-size:12px;display:none;
}
.activation-status.show{display:block}
.activation-status.success{background:rgba(76,175,80,0.15);color:#4CAF50}
.activation-status.error{background:rgba(244,67,54,0.15);color:#f44336}

/* Light Theme */
body.light-theme{
  background:linear-gradient(135deg,#f0f2f5 0%,#e8eaf6 50%,#e3f2fd 100%);color:#333;
}
body.light-theme .card{
  background:rgba(255,255,255,0.8);border-color:rgba(0,0,0,0.08);
  box-shadow:0 2px 8px rgba(0,0,0,0.06);
}
body.light-theme .info-label{color:#888}
body.light-theme .info-value{color:#333}
body.light-theme .btn-icon{background:rgba(0,0,0,0.04);border-color:rgba(0,0,0,0.1);color:#666}
body.light-theme .btn-icon:hover{background:rgba(123,104,238,0.1);color:#7b68ee}
body.light-theme .form-input{background:rgba(0,0,0,0.03);border-color:rgba(0,0,0,0.1);color:#333}
body.light-theme .form-input::placeholder{color:#aaa}
body.light-theme .form-label{color:#666}
body.light-theme .calendar{border-color:rgba(0,0,0,0.08);background:rgba(0,0,0,0.02)}
body.light-theme .calendar-header{border-bottom-color:rgba(0,0,0,0.06)}
body.light-theme .calendar-header .month-label{color:#444}
body.light-theme .calendar-nav{background:rgba(0,0,0,0.04);color:#666}
body.light-theme .calendar-dow{color:#999}
body.light-theme .calendar-day{color:#555}
body.light-theme .calendar-day.other-month{color:#ccc}
body.light-theme .calendar-day:hover{background:rgba(123,104,238,0.1);color:#333}
body.light-theme .time-select{background:rgba(0,0,0,0.03);border-color:rgba(0,0,0,0.1);color:#333}
body.light-theme .checkbox-row span{color:#555}
body.light-theme .page-header-left .data-path{color:#999}
body.light-theme .result-code{background:rgba(0,0,0,0.05)}
body.light-theme .result-code-text{color:#333}
body.light-theme .activation-input-row input{background:rgba(0,0,0,0.03);border-color:rgba(0,0,0,0.1);color:#333}
body.light-theme ::-webkit-scrollbar-thumb{background:rgba(123,104,238,0.2)}

@media(max-width:768px){
  .app-wrapper{padding:12px}
  .info-grid{grid-template-columns:1fr}
}
</style>
</head>
<body>
<div class="app-wrapper">
  <!-- Header -->
  <div class="page-header">
    <div class="page-header-left">
      <h1>MCP AI xty 授权管理器</h1>
      <div class="data-path">Data: ~/.mcp-ai-xty/auth.json</div>
    </div>
    <div class="header-actions">
      <button class="btn-icon" onclick="loadInfo()" title="刷新">&#x21bb;</button>
      <button class="btn-icon" onclick="toggleTheme()" title="切换主题">&#x263C;</button>
    </div>
  </div>

  <!-- Instance Info -->
  <div class="card">
    <div class="card-title"><span class="icon">&#x1F4CB;</span> 实例信息</div>
    <div class="info-grid">
      <div class="info-item">
        <div class="info-label">实例 UUID</div>
        <div class="info-value uuid-val" id="infoUuid">-</div>
      </div>
      <div class="info-item">
        <div class="info-label">角色</div>
        <div class="info-value" id="infoRole">-</div>
      </div>
      <div class="info-item">
        <div class="info-label">授权状态</div>
        <div class="info-value" id="infoAuthStatus">-</div>
      </div>
    </div>
  </div>

  <!-- Admin: Generate Activation Code -->
  <div class="card admin-section" id="adminSection">
    <div class="card-title"><span class="icon">&#x1F511;</span> 生成激活码</div>
    <div class="form-group">
      <div class="form-label">用户 UUID</div>
      <input type="text" class="form-input" id="targetUuid" placeholder="输入用户提供的 UUID">
    </div>
    <div class="form-group">
      <div class="form-label">到期时间</div>
      <label class="checkbox-row">
        <input type="checkbox" id="isPermanent" checked onchange="togglePermanent()">
        <span>永久（无到期时间）</span>
      </label>
      <div id="calendarWrap" style="opacity:0.3;pointer-events:none"></div>
      <div class="time-selectors" id="timeSelectors" style="opacity:0.3;pointer-events:none">
        <label>时</label>
        <select class="time-select" id="selHour" disabled></select>
        <span class="time-colon">:</span>
        <label>分</label>
        <select class="time-select" id="selMinute" disabled></select>
      </div>
    </div>
    <button class="btn btn-primary btn-full" onclick="doGenerate()">&#x1F511; 生成激活码</button>
    <div class="result-area" id="resultArea">
      <div class="result-label">&#x2705; 激活码已生成（请复制发送给用户）</div>
      <div class="result-code">
        <div class="result-code-text" id="resultCode"></div>
        <button class="btn-copy" onclick="copyResult()" title="复制激活码">&#x1F4CB;</button>
      </div>
      <div class="result-info" id="resultInfo"></div>
    </div>
  </div>

  <!-- User: Activate -->
  <div class="card user-section" id="userSection">
    <div class="card-title"><span class="icon">&#x1F511;</span> 激活</div>
    <p style="font-size:13px;color:#aaa;margin-bottom:12px;line-height:1.6">
      请将您的 UUID 发送给管理员获取激活码，然后在下方输入激活码完成激活。
    </p>
    <div class="activation-input-row">
      <input type="text" id="activationCode" placeholder="输入激活码 (例如: XXXX-XXXX-XXXX-...)">
      <button class="btn btn-primary" onclick="submitActivation()">激活</button>
    </div>
    <div class="activation-status" id="activationStatus"></div>
    <div id="currentActivation" style="margin-top:8px;font-size:12px;color:#888"></div>
  </div>
</div>

<!-- Toast -->
<div class="toast-container" id="toastContainer"></div>

<script>
(function(){
"use strict";

var cal = {year:0, month:0, selectedDate:null};
var instanceInfo = {};

function init(){
  var now = new Date();
  cal.year = now.getFullYear();
  cal.month = now.getMonth();
  populateTimeSelects();
  renderCalendar();
  loadInfo();
  setInterval(loadInfo, 30000);
  if(localStorage.getItem('auth-mgr-theme')==='light'){
    document.body.classList.add('light-theme');
  }
}

function populateTimeSelects(){
  var hSel = document.getElementById('selHour');
  var mSel = document.getElementById('selMinute');
  for(var h=0;h<24;h++){
    var o = document.createElement('option');
    o.value = pad(h); o.textContent = pad(h);
    hSel.appendChild(o);
  }
  for(var m=0;m<60;m++){
    var o2 = document.createElement('option');
    o2.value = pad(m); o2.textContent = pad(m);
    mSel.appendChild(o2);
  }
}

function pad(n){return n<10?'0'+n:''+n}

function apiGet(url){return fetch(url).then(function(r){return r.json()})}
function apiPost(url,body){
  return fetch(url,{
    method:'POST',headers:{'Content-Type':'application/json'},
    body:JSON.stringify(body||{})
  }).then(function(r){return r.json()});
}

window.loadInfo = function(){
  apiGet('/api/auth/info').then(function(data){
    instanceInfo = data;
    renderInfo(data);
    updateSections(data);
  }).catch(function(e){showToast('error','获取信息失败: '+e.message)});
};

function renderInfo(info){
  var uuidEl = document.getElementById('infoUuid');
  uuidEl.innerHTML = '';
  uuidEl.appendChild(document.createTextNode(info.instance_uuid||'-'));
  var cb = document.createElement('button');
  cb.className='btn-copy'; cb.innerHTML='&#x1F4CB;'; cb.title='复制 UUID';
  cb.onclick=function(){copyText(info.instance_uuid);showToast('success','UUID 已复制')};
  uuidEl.appendChild(cb);

  document.getElementById('infoRole').innerHTML = info.is_admin
    ? '<span class="badge badge-admin">&#x1F451; 管理员</span>'
    : '<span class="badge badge-user">&#x1F464; 用户</span>';

  var authEl = document.getElementById('infoAuthStatus');
  if(info.is_authorized){
    var remaining = info.auth_remaining || {};
    var rText = '';
    if(remaining.permanent) rText = ' (永久)';
    else if(remaining.remaining_seconds > 0) rText = ' (' + formatDuration(remaining.remaining_seconds*1000) + ')';
    authEl.innerHTML = '<span class="badge badge-authorized">&#x2705; 已授权' + rText + '</span>';
  } else {
    authEl.innerHTML = '<span class="badge badge-unauthorized">&#x274C; 未授权</span>';
  }
}

function updateSections(info){
  var adminSec = document.getElementById('adminSection');
  var userSec = document.getElementById('userSection');
  if(info.is_admin){
    adminSec.classList.add('visible');
    userSec.classList.remove('visible');
  } else {
    adminSec.classList.remove('visible');
    userSec.classList.add('visible');
    updateUserActivation(info);
  }
}

function updateUserActivation(info){
  var el = document.getElementById('currentActivation');
  if(info.has_activation_code && info.is_authorized){
    var remaining = info.auth_remaining || {};
    if(remaining.permanent){
      el.innerHTML = '&#x2705; <span style="color:#4CAF50">已激活 - 永久有效</span>';
    } else {
      el.innerHTML = '&#x2705; <span style="color:#4CAF50">已激活 - 到期: ' +
        (info.activation_expires_at||'未知') + '</span>';
    }
  } else if(info.has_activation_code && !info.is_authorized){
    el.innerHTML = '&#x26A0;&#xFE0F; <span style="color:#ffc107">激活已过期，请输入新的激活码</span>';
  } else {
    el.innerHTML = '&#x1F512; <span style="color:#ef5350">未激活 - 请联系管理员获取激活码</span>';
  }
}

// ============ Admin: Generate Activation Code ============
window.togglePermanent = function(){
  var checked = document.getElementById('isPermanent').checked;
  var calWrap = document.getElementById('calendarWrap');
  var timeSel = document.getElementById('timeSelectors');
  calWrap.style.opacity = checked ? '0.3' : '1';
  calWrap.style.pointerEvents = checked ? 'none' : 'auto';
  timeSel.style.opacity = checked ? '0.3' : '1';
  timeSel.style.pointerEvents = checked ? 'none' : 'auto';
  document.getElementById('selHour').disabled = checked;
  document.getElementById('selMinute').disabled = checked;
};

window.doGenerate = function(){
  var uuid = document.getElementById('targetUuid').value.trim();
  if(!uuid){showToast('error','请输入用户 UUID');return}
  var expiresAt = null;
  if(!document.getElementById('isPermanent').checked){
    expiresAt = buildDateTimeString();
    if(!expiresAt){showToast('error','请选择有效的到期日期');return}
  }
  apiPost('/api/auth/generate-activation-code', {uuid:uuid, expires_at:expiresAt}).then(function(r){
    if(r.success){
      document.getElementById('resultCode').textContent = r.activation_code;
      var infoText = '目标 UUID: ' + uuid;
      if(r.expires_at) infoText += '\\n到期时间: ' + r.expires_at;
      else infoText += '\\n有效期: 永久';
      document.getElementById('resultInfo').textContent = infoText;
      document.getElementById('resultArea').classList.add('show');
      showToast('success','激活码已生成');
    } else {
      showToast('error', r.error||'生成失败');
    }
  }).catch(function(e){showToast('error','请求失败: '+e.message)});
};

window.copyResult = function(){
  copyText(document.getElementById('resultCode').textContent);
  showToast('success','激活码已复制');
};

// ============ User: Activate ============
window.submitActivation = function(){
  var code = document.getElementById('activationCode').value.trim();
  var statusEl = document.getElementById('activationStatus');
  if(!code){
    statusEl.className='activation-status show error';
    statusEl.textContent='请输入激活码';
    return;
  }
  apiPost('/api/activate', {code:code}).then(function(data){
    if(data.success){
      statusEl.className='activation-status show success';
      statusEl.textContent='\\u2705 激活成功！';
      document.getElementById('activationCode').value='';
      loadInfo();
    } else {
      statusEl.className='activation-status show error';
      statusEl.textContent='\\u274c ' + (data.error||'激活失败');
    }
  }).catch(function(e){
    statusEl.className='activation-status show error';
    statusEl.textContent='请求失败: '+e.message;
  });
};

// ============ Calendar ============
function renderCalendar(){
  var container = document.getElementById('calendarWrap');
  if(!container) return;
  container.innerHTML = '';
  var calDiv = document.createElement('div');
  calDiv.className = 'calendar';

  var header = document.createElement('div');
  header.className = 'calendar-header';
  var prevBtn = document.createElement('button');
  prevBtn.className='calendar-nav'; prevBtn.innerHTML='&#x276E;'; prevBtn.type='button';
  prevBtn.onclick=function(){cal.month--;if(cal.month<0){cal.month=11;cal.year--}renderCalendar()};
  var nextBtn = document.createElement('button');
  nextBtn.className='calendar-nav'; nextBtn.innerHTML='&#x276F;'; nextBtn.type='button';
  nextBtn.onclick=function(){cal.month++;if(cal.month>11){cal.month=0;cal.year++}renderCalendar()};
  var monthLabel = document.createElement('span');
  monthLabel.className='month-label';
  var monthNames=['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'];
  monthLabel.textContent = cal.year+'年 '+monthNames[cal.month];
  header.appendChild(prevBtn); header.appendChild(monthLabel); header.appendChild(nextBtn);
  calDiv.appendChild(header);

  var grid = document.createElement('div');
  grid.className = 'calendar-grid';
  ['日','一','二','三','四','五','六'].forEach(function(d){
    var el = document.createElement('div'); el.className='calendar-dow'; el.textContent=d;
    grid.appendChild(el);
  });

  var firstDay = new Date(cal.year, cal.month, 1).getDay();
  var daysInMonth = new Date(cal.year, cal.month+1, 0).getDate();
  var prevDays = new Date(cal.year, cal.month, 0).getDate();
  var today = new Date();

  for(var p=firstDay-1;p>=0;p--){
    var el = document.createElement('div');
    el.className='calendar-day other-month'; el.textContent=prevDays-p;
    grid.appendChild(el);
  }
  for(var d=1;d<=daysInMonth;d++){
    var el2 = document.createElement('div');
    el2.className='calendar-day'; el2.textContent=d;
    if(cal.year===today.getFullYear()&&cal.month===today.getMonth()&&d===today.getDate())
      el2.classList.add('today');
    if(cal.selectedDate&&cal.selectedDate.y===cal.year&&cal.selectedDate.m===cal.month&&cal.selectedDate.d===d)
      el2.classList.add('selected');
    (function(day){
      el2.onclick=function(){cal.selectedDate={y:cal.year,m:cal.month,d:day};renderCalendar()};
    })(d);
    grid.appendChild(el2);
  }
  var total = firstDay + daysInMonth;
  var rem = (total<=35)?35-total:42-total;
  for(var n=1;n<=rem;n++){
    var el3 = document.createElement('div');
    el3.className='calendar-day other-month'; el3.textContent=n;
    grid.appendChild(el3);
  }
  calDiv.appendChild(grid);
  container.appendChild(calDiv);
}

function buildDateTimeString(){
  if(!cal.selectedDate) return null;
  return cal.selectedDate.y+'-'+pad(cal.selectedDate.m+1)+'-'+pad(cal.selectedDate.d)+' '+
    document.getElementById('selHour').value+':'+document.getElementById('selMinute').value+':00';
}

// ============ Utils ============
function formatDuration(ms){
  var s=Math.floor(ms/1000);
  var d=Math.floor(s/86400);s%=86400;
  var h=Math.floor(s/3600);s%=3600;
  var m=Math.floor(s/60);
  var parts=[];
  if(d>0)parts.push(d+'天');
  if(h>0||d>0)parts.push(pad(h)+'时');
  parts.push(pad(m)+'分');
  return parts.join(' ');
}

function showToast(type,msg){
  var c=document.getElementById('toastContainer');
  var t=document.createElement('div');
  t.className='toast toast-'+type;
  var icons={success:'\\u2705',error:'\\u274C',info:'\\u2139\\uFE0F'};
  t.innerHTML='<span>'+(icons[type]||'')+'</span><span>'+escapeHtml(msg)+'</span>';
  c.appendChild(t);
  setTimeout(function(){t.style.transition='opacity 0.3s';t.style.opacity='0';
    setTimeout(function(){t.remove()},300);
  },3500);
}

function escapeHtml(text){
  var div=document.createElement('div');
  div.appendChild(document.createTextNode(text));
  return div.innerHTML;
}

function copyText(text){
  if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(text)}
  else{var ta=document.createElement('textarea');ta.value=text;ta.style.position='fixed';
  ta.style.opacity='0';document.body.appendChild(ta);ta.select();
  try{document.execCommand('copy')}catch(e){}document.body.removeChild(ta)}
}

window.toggleTheme = function(){
  document.body.classList.toggle('light-theme');
  localStorage.setItem('auth-mgr-theme',document.body.classList.contains('light-theme')?'light':'dark');
};

if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init)}else{init()}
})();
</script>
</body>
</html>"""
