// KLIK parts catalog + SVG side-view drawings (blueprint line-art)
// Parts are loaded from localStorage klik.catalog.v1, falling back to DEFAULTS.
// The admin panel writes to that key; the configurator reads from it.

const MATERIALS = {
  'brushed-copper':  { name: 'Brushed Copper',  stroke: '#e8a878', fill: 'rgba(232,168,120,0.08)', swatch: 'linear-gradient(135deg,#c87a4e 0%,#e8a878 50%,#b06a40 100%)', tone: 'warm' },
  'polished-copper': { name: 'Polished Copper', stroke: '#ffb98a', fill: 'rgba(255,185,138,0.10)', swatch: 'linear-gradient(135deg,#f0825a 0%,#ffd9b8 45%,#c97048 100%)', tone: 'warm' },
  'brushed-steel':   { name: 'Brushed Steel',   stroke: '#c8d4e0', fill: 'rgba(200,212,224,0.06)', swatch: 'linear-gradient(135deg,#7a8896 0%,#c8d4e0 50%,#6a7886 100%)', tone: 'cool' },
  'polished-steel':  { name: 'Polished Steel',  stroke: '#e0eaf4', fill: 'rgba(224,234,244,0.08)', swatch: 'linear-gradient(135deg,#9aa8b6 0%,#f0f5fa 45%,#788896 100%)', tone: 'cool' },
  'brass':           { name: 'Brass',            stroke: '#e8c878', fill: 'rgba(232,200,120,0.08)', swatch: 'linear-gradient(135deg,#b8993f 0%,#e8c878 50%,#9a7d30 100%)', tone: 'warm' },
};

// ── Default catalog (shipped with the product) ──────────────────────────────
const DEFAULT_CATALOG = {
  heads: [
    { id: 'H-CHAMBER', label: 'HEAD / CHAMBER', name: 'Chamber', desc: 'Deep bowl. Long sessions.',    capacity: '0.35 g', price: 64, weight: 88,  vbWidth: 280, vbHeight: 200 },
    { id: 'H-COMPACT', label: 'HEAD / COMPACT', name: 'Compact', desc: 'Low-profile. Daily carry.',    capacity: '0.18 g', price: 52, weight: 64,  vbWidth: 220, vbHeight: 200 },
    { id: 'H-WIDE',    label: 'HEAD / WIDE',    name: 'Wide',    desc: 'Wide diffuser. Cool draw.',    capacity: '0.25 g', price: 58, weight: 76,  vbWidth: 260, vbHeight: 200 },
  ],
  tubes: [
    { id: 'T-16-S', label: 'TUBE / 16×127', diameter: 16, lengthMm: 127, lengthIn: '5.0"', price: 28, weight: 42, vbWidth: 380, vbHeight: 200, drawDia: 36 },
    { id: 'T-16-L', label: 'TUBE / 16×178', diameter: 16, lengthMm: 178, lengthIn: '7.0"', price: 34, weight: 58, vbWidth: 520, vbHeight: 200, drawDia: 36 },
    { id: 'T-22-S', label: 'TUBE / 22×102', diameter: 22, lengthMm: 102, lengthIn: '4.0"', price: 32, weight: 58, vbWidth: 320, vbHeight: 200, drawDia: 52 },
    { id: 'T-22-L', label: 'TUBE / 22×152', diameter: 22, lengthMm: 152, lengthIn: '6.0"', price: 38, weight: 78, vbWidth: 460, vbHeight: 200, drawDia: 52 },
  ],
  mouthpieces: [
    { id: 'M-SMALL',  label: 'MTH / SMALL',  name: 'Small',  desc: 'Tight draw. Discreet.',     price: 22, weight: 12, vbWidth: 110, vbHeight: 200 },
    { id: 'M-MEDIUM', label: 'MTH / MEDIUM', name: 'Medium', desc: 'Balanced. Standard.',        price: 24, weight: 16, vbWidth: 130, vbHeight: 200 },
    { id: 'M-LARGE',  label: 'MTH / LARGE',  name: 'Large',  desc: 'Open draw. Big clouds.',    price: 26, weight: 22, vbWidth: 150, vbHeight: 200 },
  ],
};

const CATALOG_KEY = 'klik.catalog.v1';

function loadCatalog() {
  try {
    const raw = localStorage.getItem(CATALOG_KEY);
    if (!raw) return DEFAULT_CATALOG;
    const p = JSON.parse(raw);
    if (!p.heads || !p.tubes || !p.mouthpieces) return DEFAULT_CATALOG;
    return p;
  } catch (e) { return DEFAULT_CATALOG; }
}

function saveCatalog(catalog) {
  try { localStorage.setItem(CATALOG_KEY, JSON.stringify(catalog)); } catch (e) {}
}

// Live catalog — mutable reference used by all components
let _catalog = loadCatalog();
// Rebuild PARTS_BY_ID whenever catalog changes
function rebuildPartsById() {
  const m = {};
  [..._catalog.heads, ..._catalog.tubes, ..._catalog.mouthpieces].forEach(p => { m[p.id] = p; });
  return m;
}

// ── Joint marker ────────────────────────────────────────────────────────────
const JointMarker = ({ side = 'right', color = '#7fd4ff' }) => {
  const flip = side === 'left' ? -1 : 1;
  return (
    <g>
      <rect x={-3} y={70} width={6} height={60} fill="none" stroke={color} strokeWidth={0.8} strokeDasharray="2 1.5" opacity={0.9}/>
      <circle cx={0} cy={68}  r={2.4} fill="none" stroke={color} strokeWidth={0.6}/>
      <circle cx={0} cy={132} r={2.4} fill="none" stroke={color} strokeWidth={0.6}/>
      <line x1={-1.5*flip} y1={92}  x2={1.5*flip} y2={92}  stroke={color} strokeWidth={0.6}/>
      <line x1={-1.5*flip} y1={108} x2={1.5*flip} y2={108} stroke={color} strokeWidth={0.6}/>
    </g>
  );
};

// ── TubeGeo ─────────────────────────────────────────────────────────────────
const TubeGeo = ({ part, material, showCallouts = false }) => {
  const mat = MATERIALS[material] || MATERIALS['brushed-steel'];
  const w = part.vbWidth; const d = part.drawDia;
  const top = 100 - d/2; const bot = 100 + d/2; const lipW = 10;
  return (
    <g>
      <rect x={8} y={top} width={w-16} height={d} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <rect x={2} y={top-3} width={lipW} height={d+6} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <rect x={w-12} y={top-3} width={lipW} height={d+6} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <line x1={0} y1={100} x2={w} y2={100} stroke={mat.stroke} strokeWidth={0.3} strokeDasharray="4 2 1 2" opacity={0.45}/>
      <line x1={8} y1={top+4} x2={w-8} y2={top+4} stroke={mat.stroke} strokeWidth={0.3} opacity={0.4}/>
      <line x1={8} y1={bot-4} x2={w-8} y2={bot-4} stroke={mat.stroke} strokeWidth={0.3} opacity={0.4}/>
      <g transform="translate(8 0)"><JointMarker side="left"/></g>
      <g transform={`translate(${w-8} 0)`}><JointMarker side="right"/></g>
      {showCallouts && (
        <g fontFamily="'JetBrains Mono',monospace" fontSize="9" fill={mat.stroke}>
          <line x1={8} y1={bot+18} x2={w-8} y2={bot+18} stroke={mat.stroke} strokeWidth={0.4}/>
          <line x1={8} y1={bot+14} x2={8} y2={bot+22} stroke={mat.stroke} strokeWidth={0.4}/>
          <line x1={w-8} y1={bot+14} x2={w-8} y2={bot+22} stroke={mat.stroke} strokeWidth={0.4}/>
          <text x={w/2} y={bot+32} textAnchor="middle" opacity={0.85}>{part.lengthMm} mm · {part.lengthIn}</text>
          <line x1={w/2+30} y1={top} x2={w/2+30} y2={bot} stroke={mat.stroke} strokeWidth={0.4}/>
          <text x={w/2+36} y={102} opacity={0.85}>⌀{part.diameter}</text>
        </g>
      )}
    </g>
  );
};
const Tube = ({ part, material, showCallouts=false }) => (
  <svg viewBox={`0 0 ${part.vbWidth} ${part.vbHeight}`} style={{height:'100%',display:'block'}} preserveAspectRatio="xMidYMid meet">
    <TubeGeo part={part} material={material} showCallouts={showCallouts}/>
  </svg>
);

// ── HeadGeo ─────────────────────────────────────────────────────────────────
const HeadGeo = ({ part, material, showCallouts=false }) => {
  const mat = MATERIALS[material] || MATERIALS['brushed-copper'];
  const w = part.vbWidth;
  let bowlR, neckDia;
  // Derive bowl radius from vbWidth (larger = wider bowl)
  bowlR = Math.round((w - 20) * 0.38);
  bowlR = Math.max(30, Math.min(80, bowlR));
  neckDia = part.id && part.id.includes('COMPACT') ? 26 : 30;
  const cx = bowlR + 6;
  const neckTop = 100 - neckDia/2; const neckRight = w - 6;
  return (
    <g>
      <circle cx={cx} cy={100} r={bowlR} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.2}/>
      <circle cx={cx} cy={100} r={bowlR-8} fill="none" stroke={mat.stroke} strokeWidth={0.5} opacity={0.6} strokeDasharray="3 2"/>
      <circle cx={cx} cy={100} r={bowlR-14} fill="none" stroke={mat.stroke} strokeWidth={0.4} opacity={0.4}/>
      {Array.from({length:6}).map((_,i)=>{
        const a=(i/6)*Math.PI*2; const r2=bowlR-14;
        return <line key={i} x1={cx} y1={100} x2={cx+Math.cos(a)*r2} y2={100+Math.sin(a)*r2} stroke={mat.stroke} strokeWidth={0.3} opacity={0.35}/>;
      })}
      {[bowlR-4,bowlR-22,bowlR-32].map((r,i)=>(
        <circle key={i} cx={cx} cy={100} r={Math.max(1,r)} fill="none" stroke={mat.stroke} strokeWidth={0.35} opacity={0.4}/>
      ))}
      <rect x={cx+bowlR-2} y={neckTop} width={neckRight-(cx+bowlR-2)} height={neckDia} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <rect x={neckRight-10} y={neckTop-3} width={10} height={neckDia+6} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <line x1={cx} y1={100} x2={w} y2={100} stroke={mat.stroke} strokeWidth={0.3} strokeDasharray="4 2 1 2" opacity={0.45}/>
      <g transform={`translate(${neckRight} 0)`}>
        <rect x={-3} y={100-neckDia/2-4} width={6} height={neckDia+8} fill="none" stroke="#7fd4ff" strokeWidth={0.8} strokeDasharray="2 1.5" opacity={0.9}/>
        <circle cx={0} cy={100-neckDia/2-2} r={2} fill="none" stroke="#7fd4ff" strokeWidth={0.6}/>
        <circle cx={0} cy={100+neckDia/2+2} r={2} fill="none" stroke="#7fd4ff" strokeWidth={0.6}/>
      </g>
      {showCallouts && (
        <g fontFamily="'JetBrains Mono',monospace" fontSize="9" fill={mat.stroke}>
          <line x1={cx-bowlR} y1={100+bowlR+18} x2={cx+bowlR} y2={100+bowlR+18} stroke={mat.stroke} strokeWidth={0.4}/>
          <text x={cx} y={100+bowlR+32} textAnchor="middle" opacity={0.85}>⌀{Math.round(bowlR*2*0.45)} mm</text>
          <text x={cx} y={100-bowlR-14} textAnchor="middle" opacity={0.85}>{part.name.toUpperCase()}</text>
        </g>
      )}
    </g>
  );
};
const Head = ({ part, material, showCallouts=false }) => (
  <svg viewBox={`0 0 ${part.vbWidth} ${part.vbHeight}`} style={{height:'100%',display:'block'}} preserveAspectRatio="xMidYMid meet">
    <HeadGeo part={part} material={material} showCallouts={showCallouts}/>
  </svg>
);

// ── MouthpieceGeo ───────────────────────────────────────────────────────────
const MouthpieceGeo = ({ part, material, showCallouts=false }) => {
  const mat = MATERIALS[material] || MATERIALS['polished-steel'];
  const w = part.vbWidth;
  // Derive diameters from vbWidth
  let baseDia, tipDia;
  if (part.id && part.id.includes('SMALL'))  { baseDia=22; tipDia=14; }
  else if (part.id && part.id.includes('LARGE')) { baseDia=30; tipDia=28; }
  else { baseDia=26; tipDia=20; } // MEDIUM or custom
  const flareStart=w-30; const tipX=w-6;
  return (
    <g>
      <rect x={2} y={100-baseDia/2-3} width={10} height={baseDia+6} fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <path d={`M 6 ${100-baseDia/2} L ${flareStart} ${100-baseDia/2} L ${tipX} ${100-tipDia/2} L ${tipX} ${100+tipDia/2} L ${flareStart} ${100+baseDia/2} L 6 ${100+baseDia/2} Z`}
        fill={mat.fill} stroke={mat.stroke} strokeWidth={1.1}/>
      <ellipse cx={tipX} cy={100} rx={1.5} ry={tipDia/2} fill="none" stroke={mat.stroke} strokeWidth={0.6}/>
      <line x1={0} y1={100} x2={w} y2={100} stroke={mat.stroke} strokeWidth={0.3} strokeDasharray="4 2 1 2" opacity={0.45}/>
      <g transform="translate(8 0)">
        <rect x={-3} y={100-baseDia/2-4} width={6} height={baseDia+8} fill="none" stroke="#7fd4ff" strokeWidth={0.8} strokeDasharray="2 1.5" opacity={0.9}/>
        <circle cx={0} cy={100-baseDia/2-2} r={2} fill="none" stroke="#7fd4ff" strokeWidth={0.6}/>
        <circle cx={0} cy={100+baseDia/2+2} r={2} fill="none" stroke="#7fd4ff" strokeWidth={0.6}/>
      </g>
      {showCallouts && (
        <g fontFamily="'JetBrains Mono',monospace" fontSize="9" fill={mat.stroke}>
          <text x={w/2} y={100-baseDia/2-14} textAnchor="middle" opacity={0.85}>{part.name.toUpperCase()}</text>
          <text x={tipX+4} y={102} textAnchor="start" opacity={0.85}>⌀{tipDia}</text>
        </g>
      )}
    </g>
  );
};
const Mouthpiece = ({ part, material, showCallouts=false }) => (
  <svg viewBox={`0 0 ${part.vbWidth} ${part.vbHeight}`} style={{height:'100%',display:'block'}} preserveAspectRatio="xMidYMid meet">
    <MouthpieceGeo part={part} material={material} showCallouts={showCallouts}/>
  </svg>
);

// ── Contact-point helpers ────────────────────────────────────────────────────
// Returns { left, right } — the x-coordinates of the *abutment face* of each
// part's connector collar, matching the drawn geometry exactly.
//
// HeadGeo:       right collar right-face = neckRight = w - 6
//                (no left connector — bowl end)
// TubeGeo:       left  collar left-face  = x=2  (lip rect starts at x=2)
//                right collar right-face = x=w-2 (lip rect ends at w-12+10=w-2)
// MouthpieceGeo: left  collar left-face  = x=2  (lip rect starts at x=2)
//                (no right connector — tip end)
//
// Assembly: place each segment so its leftContact === prev.rightContact
function getContacts(kind, part) {
  const w = part.vbWidth;
  if (kind === 'head') {
    return { left: null, right: w - 6 };
  }
  if (kind === 'tube') {
    return { left: 2, right: w - 2 };
  }
  // mouthpiece
  return { left: 2, right: null };
}

// Expose everything to window
Object.assign(window, {
  MATERIALS, DEFAULT_CATALOG, CATALOG_KEY,
  loadCatalog, saveCatalog,
  _getCatalog: () => _catalog,
  _setCatalog: (c) => { _catalog = c; },
  rebuildPartsById,
  getContacts,
  // Computed views updated on first call and after catalog edits
  get HEADS()       { return _catalog.heads; },
  get TUBES()       { return _catalog.tubes; },
  get MOUTHPIECES() { return _catalog.mouthpieces; },
  get PARTS_BY_ID() { return rebuildPartsById(); },
  Tube, Head, Mouthpiece, TubeGeo, HeadGeo, MouthpieceGeo, JointMarker,
});
