/* global React */

// === Constants ==============================================================
const POTENTIAL_TIERS = ['None', 'Rare', 'Epic', 'Unique', 'Legendary'];
const POTENTIAL_TIER_INDEX = { None: 0, Rare: 1, Epic: 2, Unique: 3, Legendary: 4 };
// zh-TW label with English tier in parentheses — same convention as MS Taiwan.
const POTENTIAL_TIER_LABEL_ZH = {
  None:      '無',
  Rare:      '稀有 (Rare)',
  Epic:      '史詩 (Epic)',
  Unique:    '獨特 (Unique)',
  Legendary: '傳說 (Legendary)',
};
const JOBS = ['All', 'Warrior', 'Magician', 'Bowman', 'Thief', 'Pirate'];
const TIER1_OPTIONS = ['Weapon', 'Armor', 'Decoration', 'Set-up', 'Utility'];

// MSU tier2/tier3 categories (under each tier1) — used for rule drawer selects
const CATEGORIES = {
  Weapon: {
    'One-handed Weapon': ['One-handed Sword', 'One-handed Axe', 'One-handed Mace', 'Wand', 'Staff', 'Dagger', 'Cane', 'Scepter'],
    'Two-handed Weapon': ['Two-handed Sword', 'Two-handed Axe', 'Two-Handed Blunt', 'Spear', 'Polearm', 'Bow', 'Crossbow', 'Knuckle', 'Gun', 'Dual Bowgun'],
    'Secondary Weapon':  ['Shield', 'Soul Ring', 'Magic Arrow', 'Charm', 'Far Sight'],
  },
  Armor: {
    'Armor':     ['Hat', 'Top', 'Bottom', 'Outfit', 'Shoes', 'Gloves', 'Cape'],
    'Accessory': ['Face Accessory', 'Eye Accessory', 'Earrings', 'Pendant', 'Ring', 'Belt', 'Shoulder', 'Pocket', 'Badge', 'Medal', 'Emblem', 'Mark'],
  },
};

// === Formatters =============================================================
function fmtNeso(n) {
  if (n == null || !Number.isFinite(n)) return '—';
  if (n >= 1e9) return (n / 1e9).toFixed(2) + 'B';
  if (n >= 1e6) return (n / 1e6).toFixed(2) + 'M';
  if (n >= 1e3) return (n / 1e3).toFixed(1) + 'k';
  return String(Math.round(n));
}
function fmtUsd(n) {
  if (n == null || !Number.isFinite(n)) return '—';
  if (n >= 1000) return '$' + n.toFixed(0);
  if (n >= 1) return '$' + n.toFixed(2);
  return '$' + n.toFixed(4);
}
function fmtTimeShort(d) {
  if (!d) return '--:--';
  const x = typeof d === 'string' ? new Date(d) : d;
  return String(x.getHours()).padStart(2,'0') + ':' + String(x.getMinutes()).padStart(2,'0');
}
function fmtTime(d) {
  if (!d) return '--:--:--';
  const x = typeof d === 'string' ? new Date(d) : d;
  return [x.getHours(), x.getMinutes(), x.getSeconds()].map(n => String(n).padStart(2,'0')).join(':');
}
function fmtMD(d) {
  if (!d) return '';
  const x = typeof d === 'string' ? new Date(d) : d;
  return String(x.getMonth() + 1).padStart(2,'0') + '/' + String(x.getDate()).padStart(2,'0') + ' ' + fmtTime(x);
}
function shortAddr(s) { return s ? (s.length > 10 ? s.slice(0, 8) + '…' : s) : ''; }

// === Item icon (uses MSU's static icon, falls back to monogram) =============
function ItemIcon({ src, name, size = 40, hue = 'gold' }) {
  const [errored, setErrored] = React.useState(false);
  if (src && !errored) {
    return React.createElement('img', {
      src,
      alt: '',
      width: size,
      height: size,
      onError: () => setErrored(true),
      style: { width: size, height: size, objectFit: 'contain', display: 'block', background: '#0d1411', borderRadius: 4, border: '1px solid #1f2a23' },
    });
  }
  let h = 0;
  for (let i = 0; i < (name || '').length; i++) h = (h * 31 + name.charCodeAt(i)) & 0xff;
  const initial = (name || '?').split(/[ -]/).map(w => w[0]).slice(0, 2).join('').toUpperCase();
  const stripeColor = hue === 'gold' ? 'rgba(212,166,74,0.18)' : 'rgba(140,170,160,0.15)';
  const bgColor = hue === 'gold' ? '#1a201b' : '#161c19';
  const fgColor = hue === 'gold' ? '#d4a64a' : '#9eb4a8';
  return (
    <svg width={size} height={size} viewBox="0 0 40 40" style={{ borderRadius: 4, display: 'block' }}>
      <rect width="40" height="40" fill={bgColor}/>
      <g opacity="0.7">
        {[-20,-10,0,10,20,30,40].map((x,i) => (
          <line key={i} x1={x + (h%6)} y1={0} x2={x + (h%6) + 20} y2={40} stroke={stripeColor} strokeWidth="3"/>
        ))}
      </g>
      <rect x="0.5" y="0.5" width="39" height="39" fill="none" stroke="rgba(255,255,255,0.06)"/>
      <text x="20" y="24" textAnchor="middle" fontFamily="'IBM Plex Mono', monospace" fontSize="11" fontWeight="600" fill={fgColor} letterSpacing="0.5">{initial}</text>
    </svg>
  );
}

// === API helpers ============================================================
//
// All state-changing API calls (POST/PUT/PATCH/DELETE) require an X-Tracker-Token
// header. The token is bootstrapped on first load via /api/bootstrap, which the
// server gates on Origin/Host so cross-origin pages cannot read it. We cache
// it in sessionStorage; on a 401 we re-bootstrap once and retry.
const TOKEN_KEY = 'msu-tracker-api-token';
let tokenPromise = null;
async function getApiToken(forceRefresh = false) {
  if (!forceRefresh) {
    const cached = sessionStorage.getItem(TOKEN_KEY);
    if (cached) return cached;
  }
  if (!tokenPromise) {
    tokenPromise = (async () => {
      const r = await fetch('/api/bootstrap');
      if (!r.ok) throw new Error(`bootstrap failed: HTTP ${r.status}`);
      const j = await r.json();
      sessionStorage.setItem(TOKEN_KEY, j.token);
      return j.token;
    })().finally(() => { tokenPromise = null; });
  }
  return tokenPromise;
}

async function apiGet(path) {
  const r = await fetch(path);
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  return r.json();
}

async function apiSend(method, path, body) {
  const send = async () => {
    const token = await getApiToken();
    return fetch(path, {
      method,
      headers: {
        'Content-Type': 'application/json',
        'X-Tracker-Token': token,
      },
      body: body !== undefined ? JSON.stringify(body) : undefined,
    });
  };
  let r = await send();
  if (r.status === 401) {
    // Token may have been rotated (e.g. server restart with no DB persistence).
    sessionStorage.removeItem(TOKEN_KEY);
    await getApiToken(true);
    r = await send();
  }
  if (!r.ok) {
    let msg; try { msg = (await r.json()).error; } catch { msg = `HTTP ${r.status}`; }
    throw new Error(msg);
  }
  return r.json();
}

// expose globally for the other jsx file
Object.assign(window, {
  POTENTIAL_TIERS, POTENTIAL_TIER_INDEX, POTENTIAL_TIER_LABEL_ZH,
  JOBS, TIER1_OPTIONS, CATEGORIES,
  fmtNeso, fmtUsd, fmtTime, fmtTimeShort, fmtMD, shortAddr,
  ItemIcon, apiGet, apiSend, getApiToken,
});
