// Pet MBTI — data. Exports to window: PM_C, PM_GROUPS, PM_AXES, PM_TYPES, PM_QUESTIONS, pmScore
// Plus share-link routing helpers: pmBoot, pmShareUrl, pmTypeUrl.

// ── Palette (warm cream base, four group families sampled from the artwork) ──
const PM_C = {
  bg:      '#FBE3CA',   // warm cream page
  bgWarm:  '#FFF3E6',   // hero peach / footer band
  panel:   '#FFF3E6',   // card / panel
  panelAlt:'#FCF3E4',
  line:    '#EBDCC4',
  ink:     '#3A3128',   // deep cocoa (never pure black)
  ink2:    '#6A5B4B',
  muted:   '#9C8B76',
  paw:     '#E79A5E',   // brand paw accent
  pawDeep: '#F58A50',   // buttons / brand accent
  white:   '#FFFFFF',
};

// Four temperament families → color-coded exactly like the artwork groups.
// Names/roles come from the active language (pm-i18n.jsx); colors + MBTI letters
// are language-independent.
const _GT = window.PM_TXT.groups;
const PM_GROUPS = {
  analyst:  { key:'analyst',  name:_GT.analyst.name,  role:_GT.analyst.role,  letters:'NT',
    soft:'#EDE4F8', tint:'#D8C7EE', mid:'#8B6FCB', deep:'#6B4FA8', ink:'#5A4494' },
  diplomat: { key:'diplomat', name:_GT.diplomat.name, role:_GT.diplomat.role, letters:'NF',
    soft:'#E8F1D6', tint:'#D3E0AE', mid:'#6FBF73', deep:'#5C8636', ink:'#4E7B3E' },
  sentinel: { key:'sentinel', name:_GT.sentinel.name, role:_GT.sentinel.role, letters:'SJ',
    soft:'#E4EBF8', tint:'#CBD6EE', mid:'#6F92CE', deep:'#436FB4', ink:'#335994' },
  explorer: { key:'explorer', name:_GT.explorer.name, role:_GT.explorer.role, letters:'SP',
    soft:'#FBEFCB', tint:'#F3DC9A', mid:'#F4C84A', deep:'#C48F1E', ink:'#A87A1A' },
};

function pmGroupOf(code) {
  const n = code[1], t = code[2], j = code[3];
  if (n === 'N') return t === 'T' ? 'analyst' : 'diplomat';
  return j === 'J' ? 'sentinel' : 'explorer';
}

// ── Four playful axes. first pole / second pole. ── (pole names from active lang)
const _AT = window.PM_TXT.axes;
const PM_AXES = [
  { key:'EI', a:{k:'E',name:_AT.EI.a}, b:{k:'I',name:_AT.EI.b}, label:_AT.EI.label },
  { key:'SN', a:{k:'S',name:_AT.SN.a}, b:{k:'N',name:_AT.SN.b}, label:_AT.SN.label },
  { key:'TF', a:{k:'T',name:_AT.TF.a}, b:{k:'F',name:_AT.TF.b}, label:_AT.TF.label },
  { key:'JP', a:{k:'J',name:_AT.JP.a}, b:{k:'P',name:_AT.JP.b}, label:_AT.JP.label },
];

// ── 12 statements about YOUR PET. `toward` = the letter that AGREEING supports. ──
// Statement text is pulled from the active language (pm-i18n.jsx) by id.
const _QT = window.PM_TXT.questions;
const PM_QUESTIONS = [
  { id:'q1',  axis:'EI', toward:'E', text:_QT.q1 },
  { id:'q2',  axis:'SN', toward:'S', text:_QT.q2 },
  { id:'q3',  axis:'TF', toward:'F', text:_QT.q3 },
  { id:'q4',  axis:'JP', toward:'J', text:_QT.q4 },
  { id:'q5',  axis:'EI', toward:'I', text:_QT.q5 },
  { id:'q6',  axis:'SN', toward:'N', text:_QT.q6 },
  { id:'q7',  axis:'TF', toward:'T', text:_QT.q7 },
  { id:'q8',  axis:'JP', toward:'P', text:_QT.q8 },
  { id:'q9',  axis:'EI', toward:'E', text:_QT.q9 },
  { id:'q10', axis:'SN', toward:'N', text:_QT.q10 },
  { id:'q11', axis:'TF', toward:'F', text:_QT.q11 },
  { id:'q12', axis:'JP', toward:'J', text:_QT.q12 },
];

// ── The 16 types. All display copy (nick/species/tagline/blurb/long/loves) is
// pulled from the active language (pm-i18n.jsx); code order + artwork are fixed. ──
const _T = window.PM_TXT.types;

const PM_TYPES = {};
Object.keys(_T).forEach((code) => {
  const g = pmGroupOf(code);
  PM_TYPES[code] = Object.assign({ code, group: g, colors: PM_GROUPS[g], img: `assets/types/${code}.png` }, _T[code]);
});

// ── Compatibility. matches[0] = best friend (with a reason), then two more pals. ──
// The match lists are language-independent; the "why" copy comes from pm-i18n.jsx.
const _MATCH = {
  INTP:['ENTJ','INFJ','ENTP'], INTJ:['ENFP','ENTP','INFJ'], ENTP:['INFJ','INTJ','ENFJ'], ENTJ:['INTP','INFP','ISTP'],
  INFJ:['ENFP','ENTP','INTJ'], INFP:['ENFJ','ENTJ','ISFP'], ENFP:['INFJ','INTJ','ISFJ'], ENFJ:['INFP','ISFP','ESFP'],
  ISTJ:['ESFP','ESTP','ISFJ'], ISFJ:['ESFP','ESTP','ENFP'], ESTJ:['ISFP','ISTP','ESFJ'], ESFJ:['ISFP','ISTP','ESTP'],
  ISTP:['ESFJ','ESTJ','ENTJ'], ISFP:['ENFJ','ESFJ','ESTJ'], ESTP:['ISFJ','ISTJ','ESFP'], ESFP:['ISFJ','ISTJ','ENFJ'],
};
const _MATCH_WHY = window.PM_TXT.matchWhy;
Object.keys(_MATCH).forEach((code) => {
  PM_TYPES[code].matches = _MATCH[code];
  PM_TYPES[code].matchWhy = _MATCH_WHY[code];
});

// Order for the gallery, grouped by family (matches artwork rows).
const PM_ORDER = {
  analyst:  ['INTP','INTJ','ENTP','ENTJ'],
  diplomat: ['INFJ','INFP','ENFP','ENFJ'],
  sentinel: ['ISTJ','ISFJ','ESTJ','ESFJ'],
  explorer: ['ISTP','ISFP','ESTP','ESFP'],
};

// answers: array aligned to PM_QUESTIONS: value -3..+3 (agree strength)
// returns { code, type, pct:{axisKey: 50..100 toward the winning pole} }
function pmScore(answers) {
  const sums = { EI:0, SN:0, TF:0, JP:0 };
  const maxes = { EI:0, SN:0, TF:0, JP:0 };
  PM_QUESTIONS.forEach((q, i) => {
    const v = answers[i] || 0;               // -3..3, +3 = strongly agree
    const ax = PM_AXES.find((a) => a.key === q.axis);
    const dir = q.toward === ax.b.k ? 1 : -1; // toward pole b => +
    sums[q.axis] += v * dir;
    maxes[q.axis] += 3;
  });
  let code = '';
  const pct = {};
  PM_AXES.forEach((ax) => {
    const s = sums[ax.key];
    const pole = s > 0 ? ax.b.k : s < 0 ? ax.a.k : ax.b.k; // tie -> b
    code += pole;
    const frac = maxes[ax.key] ? Math.abs(s) / maxes[ax.key] : 0;
    pct[ax.key] = Math.round(50 + frac * 50);
  });
  return { code, type: PM_TYPES[code], pct };
}

// ── Share-link routing (opaque token, 16personalities style) ─────────────────
// A shared result is fully described by its 4-letter code + the four axis
// percentages (50..100 toward the winning pole, in EI,SN,TF,JP order). Instead
// of exposing raw numbers, we pack code+percentages into a short opaque token
// (e.g. /t/INFP.html?r=k7m2qa) that the app decodes on open. The token is
// self-contained — it *is* the data, just encoded — so the exact result page
// reappears with no server or database.
const PM_AXIS_ORDER = ['EI', 'SN', 'TF', 'JP'];

// Stable code ↔ index order — do NOT reorder, it is baked into shared tokens.
const PM_CODES = ['INTP','INTJ','ENTP','ENTJ','INFJ','INFP','ENFP','ENFJ',
                  'ISTJ','ISFJ','ESTJ','ESFJ','ISTP','ISFP','ESTP','ESFP'];

// A fixed shuffled base-36 alphabet so tokens look random, not sequential.
const _B36 = (() => {
  const a = '0123456789abcdefghijklmnopqrstuvwxyz'.split('');
  let s = 0x9e3779b1;
  for (let i = a.length - 1; i > 0; i--) {
    s = (s * 1103515245 + 12345) & 0x7fffffff;
    const j = s % (i + 1);
    const t = a[i]; a[i] = a[j]; a[j] = t;
  }
  return a.join('');
})();

// Pack {code, pct} → a 6-char token: n = codeIndex, then each axis strength 0..50.
function pmEncode(code, pct) {
  let idx = PM_CODES.indexOf(code); if (idx < 0) idx = PM_CODES.indexOf('INFP');
  let n = idx;
  for (const k of PM_AXIS_ORDER) {
    const v = Math.min(50, Math.max(0, Math.round((pct && pct[k]) || 50) - 50));
    n = n * 51 + v;
  }
  let s = '';
  do { s = _B36[n % 36] + s; n = Math.floor(n / 36); } while (n > 0);
  while (s.length < 6) s = _B36[0] + s;
  return s;
}

// Unpack a token → { code, pct } or null if malformed.
function pmDecode(token) {
  if (!token) return null;
  let n = 0;
  for (const ch of token.toLowerCase()) { const d = _B36.indexOf(ch); if (d < 0) return null; n = n * 36 + d; }
  const v = [];
  for (let i = 0; i < 4; i++) { v.unshift(n % 51); n = Math.floor(n / 51); }
  if (n < 0 || n >= PM_CODES.length) return null;
  const pct = {};
  PM_AXIS_ORDER.forEach((k, i) => { pct[k] = v[i] + 50; });
  return { code: PM_CODES[n], pct };
}

// Absolute URL for a personal shared result (opaque token) or, when pct is null,
// a plain browsable type page. Uses the live origin so links work on any host.
function pmShareUrl(code, pct) {
  if (typeof window === 'undefined' || !window.location) return '';
  // Keep shares inside the active language tree (root '' or '/zh'); the token
  // itself is language-independent, so a link still opens on either tree.
  const base = window.location.origin + (window.PM_PREFIX || '') + '/t/' + code + '.html';
  return pct ? base + '?r=' + pmEncode(code, pct) : base;
}

function pmTypeUrl(code) { return pmShareUrl(code, null); }

// Parse the current URL into a boot target → { code, pct } for a specific type,
// else null. A personal result carries pct (from ?r=<token>, or legacy ?s=a,b,c,d);
// a plain /t/CODE.html (or ?t=CODE) is a browsable type page (pct null).
function pmBoot() {
  if (typeof window === 'undefined' || !window.location) return null;
  const path = window.location.pathname || '';
  const params = new URLSearchParams(window.location.search || '');

  // Opaque token carries its own code + percentages — self-describing, trust it.
  const dec = pmDecode(params.get('r'));
  if (dec && PM_TYPES[dec.code]) return dec;

  const m = path.match(/\/t\/([A-Za-z]{4})(?:\.html)?\/?$/);
  const code = ((m && m[1]) || params.get('t') || '').toUpperCase();
  if (!code || !PM_TYPES[code]) return null;

  // Legacy plain-number links (?s=a,b,c,d) stay supported.
  let pct = null;
  const s = params.get('s');
  if (s) {
    const parts = s.split(',').map((n) => parseInt(n, 10));
    if (parts.length === 4 && parts.every((n) => Number.isFinite(n) && n >= 50 && n <= 100)) {
      pct = {};
      PM_AXIS_ORDER.forEach((k, i) => { pct[k] = parts[i]; });
    }
  }
  return { code, pct };
}

Object.assign(window, {
  PM_C, PM_GROUPS, PM_AXES, PM_TYPES, PM_QUESTIONS, PM_ORDER, PM_AXIS_ORDER, PM_CODES,
  pmScore, pmGroupOf, pmEncode, pmDecode, pmShareUrl, pmTypeUrl, pmBoot,
});
