// Nexus — Shared API auth module (JWT Bearer + auto-refresh via HttpOnly cookie)
// Include before page-specific scripts: <script src="/app/api.js"></script>

const API = window.location.origin + '/api';

function _getTokens() {
  return {
    access: localStorage.getItem('access_token') || '',
    // Refresh token is in HttpOnly cookie — not accessible from JS
    expires: parseInt(localStorage.getItem('token_expires') || '0'),
  };
}

function _isTokenExpiring() {
  // Refresh if token expires within 2 minutes
  return Date.now() > _getTokens().expires - 120000;
}

async function _refreshTokens() {
  try {
    // Refresh token is auto-sent as HttpOnly cookie by browser (same-origin)
    const res = await fetch(API + '/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    });
    if (!res.ok) return false;
    const data = await res.json();
    if (!data.success) return false;
    localStorage.setItem('access_token', data.access_token);
    localStorage.setItem('token_expires', String(Date.now() + (data.expires_in || 1800) * 1000));
    return true;
  } catch {
    return false;
  }
}

function apiHeaders() {
  const { access } = _getTokens();
  return { 'Authorization': 'Bearer ' + access };
}

function apiHeadersJSON() {
  return { ...apiHeaders(), 'Content-Type': 'application/json' };
}

async function apiFetch(url, options = {}) {
  // Auto-refresh if token is about to expire
  if (_isTokenExpiring()) {
    const refreshed = await _refreshTokens();
    if (!refreshed) {
      _logout();
      return null;
    }
  }

  // Add auth headers
  options.headers = { ...apiHeaders(), ...(options.headers || {}) };

  const res = await fetch(url, options);

  // Record session activity on successful API calls
  if (res.status !== 401) {
    _recordActivity();
  }

  // If 401, try refresh once
  if (res.status === 401) {
    const refreshed = await _refreshTokens();
    if (refreshed) {
      options.headers = { ...apiHeaders(), ...(options.headers || {}) };
      return fetch(url, options);
    }
    _logout();
    return null;
  }

  return res;
}

function _logout() {
  localStorage.removeItem('access_token');
  localStorage.removeItem('token_expires');
  localStorage.removeItem('admin');
  localStorage.removeItem('last_activity');
  window.location.href = '/app/login.html';
}

function doLogout() {
  // Notify backend (best-effort, don't block)
  // Refresh token is in HttpOnly cookie — auto-sent by browser
  const { access } = _getTokens();
  if (access) {
    try {
      fetch(API + '/auth/logout', {
        method: 'POST',
        keepalive: true,
        credentials: 'same-origin',
      });
    } catch {}
  }
  _logout();
}

// ── Session Inactivity Timeout ──
// Auto-logout after 8 hours of no API activity
const SESSION_MAX_AGE_MS = 8 * 60 * 60 * 1000; // 8 hours

function _checkSessionAge() {
  const lastActivity = parseInt(localStorage.getItem('last_activity') || '0');
  if (lastActivity && Date.now() - lastActivity > SESSION_MAX_AGE_MS) {
    _logout();
    return false;
  }
  return true;
}

function _recordActivity() {
  localStorage.setItem('last_activity', String(Date.now()));
}

// Backward-compatible alias — try refresh cookie if no access token
const token = localStorage.getItem('access_token') || '';
if (!token) {
  // No access token — try refresh via HttpOnly cookie (auto-sent by browser)
  _refreshTokens().then(ok => {
    if (!ok) _logout();
    else _recordActivity();
  });
} else if (!_checkSessionAge()) {
  // Session expired — will redirect to login via _checkSessionAge → _logout
} else {
  _recordActivity();
}

// ── Monaco Editor 预加载（后台，不阻塞页面） ──
(function preloadMonaco(){
  if(window._monacoLoading||window._monacoReady)return;
  window._monacoLoading=true;
  const s=document.createElement('script');
  s.src='https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js';
  s.onload=function(){
    require.config({paths:{vs:'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs'}});
    require(['vs/editor/editor.main'],function(){
      window.monaco=monaco;
      window._monacoReady=true;
    });
  };
  document.head.appendChild(s);
})();

// ── Global Toast Notification System ──
// Call toast('success', '操作成功') or toast('error', '操作失败') from any page.

let _toastContainer = null;
let _toastId = 0;

function _ensureToastContainer() {
  if (_toastContainer) return;
  _toastContainer = document.createElement('div');
  _toastContainer.id = 'toastContainer';
  _toastContainer.className = 'fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none';
  _toastContainer.style.maxWidth = '380px';
  document.body.appendChild(_toastContainer);
}

function toast(type, message, duration) {
  _ensureToastContainer();
  duration = duration || 3000;
  const id = ++_toastId;

  const colors = {
    success: 'bg-green-900/90 border-green-700/50 text-green-200',
    error:   'bg-red-900/90 border-red-700/50 text-red-200',
    warning: 'bg-yellow-900/90 border-yellow-700/50 text-yellow-200',
    info:    'bg-blue-900/90 border-blue-700/50 text-blue-200',
  };
  const icons = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' };

  const el = document.createElement('div');
  el.id = 'toast-' + id;
  el.className = `pointer-events-auto flex items-center gap-2 px-4 py-3 rounded-xl border shadow-lg text-sm backdrop-blur-sm transition-all duration-300 opacity-0 translate-x-4 ${colors[type] || colors.info}`;
  el.innerHTML = `<span class="text-base">${icons[type] || 'ℹ'}</span><span class="flex-1">${_toastEsc(message)}</span><button onclick="dismissToast(${id})" class="text-current opacity-50 hover:opacity-100 text-xs ml-2">✕</button>`;

  _toastContainer.appendChild(el);

  // Animate in
  requestAnimationFrame(() => {
    el.classList.remove('opacity-0', 'translate-x-4');
    el.classList.add('opacity-100', 'translate-x-0');
  });

  // Auto-dismiss
  if (duration > 0) {
    setTimeout(() => dismissToast(id), duration);
  }

  return id;
}

function dismissToast(id) {
  const el = document.getElementById('toast-' + id);
  if (!el) return;
  el.classList.add('opacity-0', 'translate-x-4');
  el.classList.remove('opacity-100', 'translate-x-0');
  setTimeout(() => el.remove(), 300);
}

function _toastEsc(s) { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
