// Shared chrome: TabBar, StatusPill, etc.

function TabBar({ tabs, active, onChange }) {
  return (
    <nav className="tabbar">
      {tabs.map((t) => (
        <button
          key={t.id}
          className={'tab ' + (active === t.id ? 'on' : '')}
          onClick={() => onChange(t.id)}
        >
          <Icon name={t.icon} />
          <span>{t.label}</span>
        </button>
      ))}
    </nav>
  );
}

function StatusPill({ status, small }) {
  const map = {
    pending:  { label: 'Chờ duyệt', cls: 'pending' },
    approved: { label: 'Đã duyệt', cls: 'approved' },
    rejected: { label: 'Từ chối',  cls: 'rejected' },
  };
  const s = map[status];
  return <span className={'pill ' + s.cls + (small ? ' sm' : '')}>{s.label}</span>;
}

function Icon({ name }) {
  const stroke = 'currentColor';
  const sw = 1.6;
  switch (name) {
    case 'home':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
          <path d="M3 11l9-7 9 7" /><path d="M5 10v9h14v-9" />
        </svg>
      );
    case 'list':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
          <line x1="8" y1="6" x2="21" y2="6" /><line x1="8" y1="12" x2="21" y2="12" /><line x1="8" y1="18" x2="21" y2="18" />
          <circle cx="4" cy="6" r="1" /><circle cx="4" cy="12" r="1" /><circle cx="4" cy="18" r="1" />
        </svg>
      );
    case 'wallet':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
          <path d="M3 7a2 2 0 012-2h13v4" /><path d="M3 7v10a2 2 0 002 2h15V9H5a2 2 0 01-2-2z" /><circle cx="16" cy="14" r="1.2" fill={stroke} />
        </svg>
      );
    case 'check':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
          <path d="M4 12l5 5L20 6" />
        </svg>
      );
    case 'users':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
          <circle cx="9" cy="8" r="3.2" /><path d="M3 20c0-3.3 2.7-6 6-6s6 2.7 6 6" />
          <circle cx="17" cy="9" r="2.4" /><path d="M15 20c0-2.6 1.7-5 4-5" />
        </svg>
      );
    default:
      return null;
  }
}

window.TabBar = TabBar;
window.StatusPill = StatusPill;
window.Icon = Icon;

// ---------- Selfie compression ----------
// Resize a video frame onto a canvas so the longer edge is ≤ 640px and
// encode JPEG q=0.5. Returns a Blob (~30–60KB on typical phones).
async function compressSelfie(videoEl) {
  const w = videoEl.videoWidth, h = videoEl.videoHeight;
  if (!w || !h) throw new Error('camera_not_ready');
  const longEdge = 640;
  const scale = Math.min(1, longEdge / Math.max(w, h));
  const cw = Math.round(w * scale), ch = Math.round(h * scale);
  const canvas = document.createElement('canvas');
  canvas.width = cw; canvas.height = ch;
  canvas.getContext('2d').drawImage(videoEl, 0, 0, cw, ch);
  return await new Promise((resolve, reject) => {
    canvas.toBlob(
      (b) => b ? resolve(b) : reject(new Error('encode_failed')),
      'image/jpeg', 0.5
    );
  });
}

// ---------- Geolocation ----------
// Returns { lat, lng, accuracy } or null on denial / timeout. Never throws.
async function getGps(timeoutMs = 5000) {
  if (!navigator.geolocation) return null;
  return await new Promise((resolve) => {
    let done = false;
    const finish = (val) => { if (!done) { done = true; resolve(val); } };
    navigator.geolocation.getCurrentPosition(
      (pos) => finish({
        lat: pos.coords.latitude,
        lng: pos.coords.longitude,
        accuracy: pos.coords.accuracy,
      }),
      () => finish(null),
      { enableHighAccuracy: false, timeout: timeoutMs, maximumAge: 60000 }
    );
    setTimeout(() => finish(null), timeoutMs + 200);
  });
}

// ---------- Multipart submit ----------
// Posts a FormData to a same-origin endpoint, including credentials.
// Resolves with parsed JSON or rejects with parsed error body.
async function postMultipart(url, fields) {
  const fd = new FormData();
  for (const [k, v] of Object.entries(fields)) {
    if (v == null) continue;
    if (v instanceof Blob) fd.append(k, v, k === 'selfie' ? 'selfie.jpg' : '');
    else fd.append(k, String(v));
  }
  const r = await fetch(url, {
    method: 'POST',
    body: fd,
    credentials: 'include',
  });
  const text = await r.text();
  let body = {};
  try { body = text ? JSON.parse(text) : {}; } catch { body = { raw: text }; }
  if (!r.ok) {
    const err = new Error(body.error || 'request_failed');
    err.status = r.status;
    err.body = body;
    throw err;
  }
  return body;
}

window.compressSelfie = compressSelfie;
window.getGps = getGps;
window.postMultipart = postMultipart;
