// tokens.jsx — Design tokens, color, type, format helpers
// Aesthetic: "Quiet finance" — warm cream paper, deep ink, muted sage accent.
// Numbers in editorial serif (Instrument Serif), UI in Inter.

const tokens = {
  // Surfaces
  paper:   '#F4F0E8', // warm cream
  card:    '#FAF7F1', // raised paper
  inset:   '#EFEAE0', // recessed
  divider: 'rgba(33, 28, 22, 0.08)',
  hairline:'rgba(33, 28, 22, 0.06)',

  // Ink
  ink:     '#1F1B14', // primary text
  ink2:    '#5A5247', // secondary
  ink3:    '#8E867A', // tertiary
  ink4:    '#B5AFA3', // quaternary / placeholder

  // Sage accent (single)
  sage:    '#6F7B5F',
  sageDeep:'#4F5942',
  sageSoft:'#E2E5D6',

  // Burden colors (subtle, not neon)
  ok:      '#5E7A4F', // green
  warn:    '#B07B2C', // amber
  bad:     '#A14438', // rust

  // Type
  serif:   '"Newsreader", "Source Serif Pro", Georgia, serif',
  sans:    '"Inter", -apple-system, system-ui, sans-serif',
  mono:    '"JetBrains Mono", ui-monospace, "SF Mono", monospace',

  // Radii
  r:   { sm: 8, md: 12, lg: 16, xl: 22, pill: 9999 },
};

// EU number format: 1.234,56 €
function fmtEUR(n, { sym = true, decimals = 2 } = {}) {
  if (n == null || isNaN(n)) n = 0;
  const sign = n < 0 ? '-' : '';
  const abs = Math.abs(n);
  const fixed = abs.toFixed(decimals);
  const [int, dec] = fixed.split('.');
  const intGrouped = int.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
  const body = decimals > 0 ? `${intGrouped},${dec}` : intGrouped;
  return sym ? `${sign}${body} €` : `${sign}${body}`;
}

function fmtEURShort(n) {
  if (n == null || isNaN(n)) n = 0;
  const abs = Math.abs(n);
  if (abs >= 1000) {
    const k = abs / 1000;
    const v = k >= 10 ? k.toFixed(1) : k.toFixed(2);
    return `${n < 0 ? '-' : ''}${v.replace('.', ',')}k €`;
  }
  return fmtEUR(n, { decimals: 0 });
}

function fmtPct(n, decimals = 0) {
  if (n == null || isNaN(n)) return '0%';
  return `${n.toFixed(decimals).replace('.', ',')}%`;
}

// Date helpers
const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const DOW_SHORT = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];

function fmtDate(d) {
  // "12 Apr"
  if (typeof d === 'string') d = new Date(d);
  return `${d.getDate()} ${MONTHS_SHORT[d.getMonth()]}`;
}
function fmtDateLong(d) {
  if (typeof d === 'string') d = new Date(d);
  return `${d.getDate()} ${MONTHS_SHORT[d.getMonth()]} ${d.getFullYear()}`;
}
function fmtMonth(yyyymm) {
  const [y, m] = yyyymm.split('-').map(Number);
  return `${MONTHS[m-1]} ${y}`;
}
function todayISO() {
  const d = new Date();
  const yyyy = d.getFullYear();
  const mm = String(d.getMonth()+1).padStart(2,'0');
  const dd = String(d.getDate()).padStart(2,'0');
  return `${yyyy}-${mm}-${dd}`;
}
function ymKey(d) {
  if (typeof d === 'string') d = new Date(d);
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;
}
function daysBetween(a, b) {
  const A = new Date(a); A.setHours(0,0,0,0);
  const B = new Date(b); B.setHours(0,0,0,0);
  return Math.round((B - A) / 86400000);
}

// Compute the next billing date for a sub given billing day-of-month
function nextBillingDate(billingDay, fromDate = new Date()) {
  const today = new Date(fromDate);
  today.setHours(0,0,0,0);
  const y = today.getFullYear();
  const m = today.getMonth();
  // try this month first
  const lastOfThis = new Date(y, m+1, 0).getDate();
  let day = Math.min(billingDay, lastOfThis);
  let candidate = new Date(y, m, day);
  if (candidate < today) {
    const lastOfNext = new Date(y, m+2, 0).getDate();
    day = Math.min(billingDay, lastOfNext);
    candidate = new Date(y, m+1, day);
  }
  return candidate;
}

function daysUntilBilling(billingDay, fromDate = new Date()) {
  return daysBetween(fromDate, nextBillingDate(billingDay, fromDate));
}

// Burden bucket from percentage (with thresholds from settings)
function burdenColor(pct, thresholds = { low: 5, high: 10 }) {
  if (pct < thresholds.low) return tokens.ok;
  if (pct < thresholds.high) return tokens.warn;
  return tokens.bad;
}
function burdenLabel(pct, thresholds = { low: 5, high: 10 }) {
  if (pct < thresholds.low) return 'light';
  if (pct < thresholds.high) return 'moderate';
  return 'heavy';
}

Object.assign(window, {
  tokens, fmtEUR, fmtEURShort, fmtPct,
  fmtDate, fmtDateLong, fmtMonth, todayISO, ymKey, daysBetween,
  nextBillingDate, daysUntilBilling, burdenColor, burdenLabel,
  MONTHS, MONTHS_SHORT, DOW_SHORT,
});
