📄 src/ui/loadingTab.ts
import { LOADING_SUBTITLE, LOADING_TITLE, formatMegabytes } from './strings';

/**
 * A helper tab that acts as the loading indicator: opened synchronously on
 * click (so it is never popup-blocked), shows a spinner + progress bar while
 * the PDF streams, and is then navigated to the finished PDF blob.
 */

export interface LoadingTab {
  /** Navigate the helper tab to the finished PDF. */
  openPdf(blobUrl: string): void;
  /** Progress callback, wired into the PDF download. */
  setProgress(loadedBytes: number, totalBytes: number | null): void;
  /** Did the user close the helper tab? */
  isClosed(): boolean;
  close(): void;
}

const LOADING_HTML = `<!doctype html>
<html lang="sv">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${LOADING_TITLE}</title>
<style>
  :root { color-scheme: light; }
  * { box-sizing: border-box; margin: 0; }
  body {
    font-family: 'Lato', -apple-system, 'Segoe UI', system-ui, sans-serif;
    background: #f4f4f5;
    color: #2a2d31;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .card {
    background: #fff;
    border-radius: 14px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
    padding: 40px 48px 32px;
    width: min(420px, calc(100vw - 48px));
    text-align: center;
  }
  .spinner {
    width: 44px;
    height: 44px;
    margin: 0 auto 20px;
    border-radius: 50%;
    border: 4px solid #f0d5d6;
    border-top-color: #bd2428;
    animation: vfpe-spin 0.9s linear infinite;
  }
  @keyframes vfpe-spin { to { transform: rotate(360deg); } }
  h1 { font-size: 20px; font-weight: 700; margin-bottom: 6px; }
  .sub { font-size: 14px; color: #6b6f76; margin-bottom: 22px; line-height: 1.5; }
  .track { height: 8px; border-radius: 999px; background: #ececed; overflow: hidden; }
  .bar {
    height: 100%;
    width: 0%;
    border-radius: 999px;
    background: #bd2428;
    transition: width 0.2s ease;
  }
  .bar.indeterminate { width: 35%; animation: vfpe-slide 1.1s ease-in-out infinite; }
  @keyframes vfpe-slide {
    0% { margin-left: -35%; }
    100% { margin-left: 100%; }
  }
  .status { font-size: 13px; color: #6b6f76; margin-top: 12px; min-height: 1em; }
  footer { margin-top: 26px; font-size: 12px; color: #a3a6ac; }
</style>
</head>
<body>
  <main class="card">
    <div class="spinner" role="status" aria-label="Laddar"></div>
    <h1>${LOADING_TITLE}</h1>
    <p class="sub">${LOADING_SUBTITLE}</p>
    <div class="track"><div class="bar indeterminate" id="vfpe-bar"></div></div>
    <p class="status" id="vfpe-status">Hämtar…</p>
    <footer>VF PDF-opener</footer>
  </main>
</body>
</html>`;

/** Open the loading tab. Returns null if the browser blocked the popup. */
export function openLoadingTab(): LoadingTab | null {
  let win: Window | null = null;
  try {
    win = window.open('', '_blank');
  } catch {
    return null;
  }
  if (!win) return null;

  try {
    const doc = win.document;
    doc.open();
    doc.write(LOADING_HTML);
    doc.close();
  } catch {
    try {
      win.close();
    } catch {
      // Ignore — the tab will just sit there empty until closed manually.
    }
    return null;
  }

  const bar = win.document.getElementById('vfpe-bar') as HTMLDivElement | null;
  const status = win.document.getElementById('vfpe-status') as HTMLParagraphElement | null;

  return {
    openPdf(blobUrl: string) {
      try {
        win!.location.replace(blobUrl);
      } catch {
        // The tab was closed between the check and the navigation — ignore.
      }
    },
    setProgress(loadedBytes: number, totalBytes: number | null) {
      if (!bar || !status) return;
      if (totalBytes !== null && totalBytes > 0) {
        bar.classList.remove('indeterminate');
        const percent = Math.min(100, Math.round((loadedBytes / totalBytes) * 100));
        bar.style.width = `${percent}%`;
        status.textContent = `${formatMegabytes(loadedBytes)} av ${formatMegabytes(totalBytes)}`;
      } else {
        status.textContent = `${formatMegabytes(loadedBytes)} hämtat…`;
      }
    },
    isClosed() {
      return win!.closed;
    },
    close() {
      if (!win!.closed) win!.close();
    },
  };
}