/* pet.jsx — pawpost pet doodle engine (sibling of bouquet.jsx)
   Composable SVG built from interchangeable parts (head / ears / eyes / nose /
   mouth / markings / coat / collar / hat / whiskers), same flat-fill + ink-outline
   language as the petalpost flower system. Lives in /shared (served at /shared
   regardless of host) so it's one canonical renderer, like the bouquet.

   IMPORTANT: outline ink is a CONCRETE hex, not `var(--ink)`. The print pipeline
   rasterizes this SVG off-document (renderFrontComposite serializes it to a data
   URL), where CSS custom properties don't resolve — a var() stroke would vanish on
   the printed card. Keep all colors concrete here. */

const COATS = {
  cream:    '#F0E2B5',
  butter:   '#F1C24B',
  ginger:   '#D6823A',
  rust:     '#B25223',
  brown:    '#7B4A28',
  chocolate:'#4E3220',
  charcoal: '#332A22',
  grey:     '#B8A992',
  white:    '#F6EFD7',
  black:    '#23190F',
};

/* Marking accent colors — what shows over the coat */
const MARKS = {
  white:    '#F6EFD7',
  cream:    '#EAD8A6',
  dark:     '#2A1F14',
  black:    '#1B130A',
};

const STROKE = '#221C12';   /* outline ink — concrete (see header note) */
const STROKE_W = 4;

/* helpers */
const Stroke = (props) => ({
  fill: 'none',
  stroke: STROKE,
  strokeWidth: STROKE_W,
  strokeLinecap: 'round',
  strokeLinejoin: 'round',
  ...props,
});
const Fill = (color, extra={}) => ({
  fill: color,
  stroke: STROKE,
  strokeWidth: STROKE_W,
  strokeLinecap: 'round',
  strokeLinejoin: 'round',
  ...extra,
});

/* ── HEAD shapes ──────────────────────────────────────────────────── */

/* return the outline path string for marking overlays + the head fill */
function getHeadPath(shape) {
  switch (shape) {
    case 'round':  return "M120 60 C 80 60, 56 92, 56 124 C 56 162, 86 190, 120 190 C 154 190, 184 162, 184 124 C 184 92, 160 60, 120 60 Z";
    case 'square': return "M120 64 C 78 64, 56 92, 56 118 C 56 138, 60 158, 72 174 C 86 192, 102 198, 120 198 C 138 198, 154 192, 168 174 C 180 158, 184 138, 184 118 C 184 92, 162 64, 120 64 Z";
    case 'narrow': return "M120 60 C 92 60, 76 90, 76 128 C 76 168, 96 198, 120 198 C 144 198, 164 168, 164 128 C 164 90, 148 60, 120 60 Z";
    case 'heart':  return "M120 70 C 78 64, 50 92, 56 130 C 62 168, 96 196, 120 198 C 144 196, 178 168, 184 130 C 190 92, 162 64, 120 70 Z";
    case 'oval':
    default:       return "M120 64 C 76 64, 50 96, 50 130 C 50 168, 84 196, 120 196 C 156 196, 190 168, 190 130 C 190 96, 164 64, 120 64 Z";
  }
}

/* ── EARS — drawn BEFORE the head so the head occludes their base ─ */

function Ears({ ears, coat }) {
  const fill = COATS[coat] || COATS.cream;
  const props = Fill(fill);
  const innerFill = adjustColor(fill, -.18); /* darker inner ear */
  const inner = { ...Fill(innerFill), strokeWidth: 0 };

  switch (ears) {
    case 'pointy': // cat / shepherd
      return (
        <g>
          <path d="M62 110 L 52 56 L 92 92 Z" {...props} />
          <path d="M178 110 L 188 56 L 148 92 Z" {...props} />
          <path d="M66 100 L 62 70 L 84 90 Z" {...inner} />
          <path d="M174 100 L 178 70 L 156 90 Z" {...inner} />
        </g>
      );
    case 'folded': // scottish fold
      return (
        <g>
          <path d="M70 96 C 64 80, 70 70, 86 78 C 96 84, 96 96, 86 100 Z" {...props} />
          <path d="M170 96 C 176 80, 170 70, 154 78 C 144 84, 144 96, 154 100 Z" {...props} />
        </g>
      );
    case 'perky': // terrier
      return (
        <g>
          <path d="M70 100 C 60 70, 70 56, 92 76 C 100 84, 100 96, 92 104 Z" {...props} />
          <path d="M170 100 C 180 70, 170 56, 148 76 C 140 84, 140 96, 148 104 Z" {...props} />
        </g>
      );
    case 'floppy': // lab / spaniel
      return (
        <g>
          <path d="M70 96 C 36 100, 28 154, 56 168 C 72 176, 82 158, 84 130 Z" {...props} />
          <path d="M170 96 C 204 100, 212 154, 184 168 C 168 176, 158 158, 156 130 Z" {...props} />
        </g>
      );
    case 'long-floppy': // basset
      return (
        <g>
          <path d="M70 100 C 20 110, 14 184, 50 196 C 76 204, 86 178, 86 142 Z" {...props} />
          <path d="M170 100 C 220 110, 226 184, 190 196 C 164 204, 154 178, 154 142 Z" {...props} />
        </g>
      );
    default:
      return null;
  }
}

/* ── EYES ─────────────────────────────────────────────────────────── */

function Eyes({ eyes }) {
  const dark = '#1B130A';
  switch (eyes) {
    case 'happy': // closed ^_^
      return (
        <g {...Stroke({ strokeWidth: 4 })}>
          <path d="M88 130 C 94 122, 104 122, 110 130" />
          <path d="M130 130 C 136 122, 146 122, 152 130" />
        </g>
      );
    case 'big':
      return (
        <g>
          <ellipse cx="98" cy="132" rx="8" ry="9" fill={dark} />
          <ellipse cx="142" cy="132" rx="8" ry="9" fill={dark} />
          <circle cx="100" cy="129" r="2.4" fill="#FFF" />
          <circle cx="144" cy="129" r="2.4" fill="#FFF" />
        </g>
      );
    case 'almond':
      return (
        <g>
          <path d="M86 130 C 92 124, 108 124, 114 130 C 108 138, 92 138, 86 130 Z" fill={dark} />
          <path d="M126 130 C 132 124, 148 124, 154 130 C 148 138, 132 138, 126 130 Z" fill={dark} />
        </g>
      );
    case 'sleepy':
      return (
        <g {...Stroke({ strokeWidth: 4 })}>
          <path d="M88 132 C 96 138, 106 138, 110 132" />
          <path d="M130 132 C 138 138, 148 138, 152 132" />
        </g>
      );
    case 'dot':
    default:
      return (
        <g>
          <circle cx="98" cy="132" r="5" fill={dark} />
          <circle cx="142" cy="132" r="5" fill={dark} />
        </g>
      );
  }
}

/* ── NOSE ─────────────────────────────────────────────────────────── */

function Nose({ nose }) {
  const ink = '#1B130A';
  switch (nose) {
    case 'tri':   // cat
      return <path d="M115 152 L 125 152 L 120 160 Z" fill={ink} stroke={ink} strokeWidth="2" strokeLinejoin="round" />;
    case 'heart':
      return <path d="M120 162 C 110 152, 110 144, 116 144 C 118 144, 120 146, 120 148 C 120 146, 122 144, 124 144 C 130 144, 130 152, 120 162 Z" fill={ink} stroke={ink} strokeWidth="1.5" strokeLinejoin="round" />;
    case 'bean':
    default:
      return <ellipse cx="120" cy="154" rx="8" ry="5" fill={ink} />;
  }
}

/* ── MOUTH ────────────────────────────────────────────────────────── */

function Mouth({ mouth, nose }) {
  const noseY = (nose === 'tri') ? 160 : (nose === 'heart' ? 162 : 159);
  switch (mouth) {
    case 'cat':
      return (
        <g {...Stroke({ strokeWidth: 3.2 })}>
          <path d={`M120 ${noseY} L 120 168`} />
          <path d={`M120 168 C 116 174, 110 174, 106 170`} />
          <path d={`M120 168 C 124 174, 130 174, 134 170`} />
        </g>
      );
    case 'tongue':
      return (
        <g>
          <path d={`M120 ${noseY} L 120 168`} {...Stroke({ strokeWidth: 3.2 })} />
          <path d={`M108 168 C 112 178, 128 178, 132 168`} {...Stroke({ strokeWidth: 3.2 })} />
          <path d="M114 170 C 114 184, 126 184, 126 170 Z" fill="#E07A88" stroke="#B85D6A" strokeWidth="1.5" />
        </g>
      );
    case 'smile':
      return (
        <g {...Stroke({ strokeWidth: 3.2 })}>
          <path d={`M120 ${noseY} L 120 167`} />
          <path d={`M104 168 C 110 178, 130 178, 136 168`} />
        </g>
      );
    case 'none':
      return null;
    default:
      return (
        <g {...Stroke({ strokeWidth: 3.2 })}>
          <path d={`M120 ${noseY} L 120 168`} />
          <path d={`M108 168 C 114 174, 126 174, 132 168`} />
        </g>
      );
  }
}

/* ── WHISKERS (optional) ─────────────────────────────────────────── */

function Whiskers({ on }) {
  if (!on) return null;
  return (
    <g {...Stroke({ strokeWidth: 2.4 })}>
      <path d="M70 158 L 102 156" opacity=".85" />
      <path d="M70 168 L 102 164" opacity=".85" />
      <path d="M170 158 L 138 156" opacity=".85" />
      <path d="M170 168 L 138 164" opacity=".85" />
    </g>
  );
}

/* ── MARKINGS — overlayed on head, clipped to head path ──────────── */

function Markings({ marking, markColor, headShape, idSuffix }) {
  if (marking === 'none' || !marking) return null;
  const fill = MARKS[markColor] || MARKS.white;
  const clipId = `head-clip-${idSuffix}`;
  const headPath = getHeadPath(headShape);

  let shapes = null;
  switch (marking) {
    case 'patch': // eye patch over one eye
      shapes = (
        <ellipse cx="98" cy="132" rx="20" ry="18" fill={fill} stroke={STROKE} strokeWidth="0" />
      );
      break;
    case 'spots':
      shapes = (
        <g fill={fill} stroke="none">
          <circle cx="78" cy="120" r="8" />
          <circle cx="160" cy="148" r="6" />
          <circle cx="130" cy="184" r="7" />
          <circle cx="92" cy="178" r="5" />
        </g>
      );
      break;
    case 'tabby': // forehead stripes
      shapes = (
        <g {...Stroke({ stroke: MARKS.dark, strokeWidth: 4 })}>
          <path d="M120 70 L 120 100" />
          <path d="M104 78 L 108 102" />
          <path d="M136 78 L 132 102" />
          <path d="M88 92 L 94 110" />
          <path d="M152 92 L 146 110" />
        </g>
      );
      break;
    case 'tuxedo': // lower face white
      shapes = (
        <path d="M50 150 C 70 184, 170 184, 190 150 C 190 196, 50 196, 50 150 Z" fill={fill} stroke="none" />
      );
      break;
    case 'mask': // dark band over eyes (common dog marking)
      shapes = (
        <path d="M60 124 C 90 116, 150 116, 180 124 L 180 140 C 150 132, 90 132, 60 140 Z" fill={MARKS.dark} stroke="none" />
      );
      break;
    default:
      return null;
  }

  return (
    <g clipPath={`url(#${clipId})`}>
      <defs>
        <clipPath id={clipId}>
          <path d={headPath} />
        </clipPath>
      </defs>
      {shapes}
    </g>
  );
}

/* ── HAT ─────────────────────────────────────────────────────────── */

function Hat({ hat, hatColor='#B25223' }) {
  if (!hat || hat === 'none') return null;
  switch (hat) {
    case 'party':
      return (
        <g>
          <path d="M120 14 L 88 78 L 152 78 Z"
                fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M99 56 L 141 56" stroke="#FFF" strokeWidth="3" opacity=".75"/>
          <path d="M105 38 L 135 38" stroke="#FFF" strokeWidth="3" opacity=".75"/>
          <circle cx="120" cy="14" r="7" fill="#F1C24B" stroke={STROKE} strokeWidth="2.5"/>
        </g>
      );
    case 'beanie':
      return (
        <g>
          <path d="M62 82 C 62 38, 178 38, 178 82 Z"
                fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M62 82 L 178 82" stroke={STROKE} strokeWidth="3"/>
          <path d="M90 50 L 92 80" stroke={STROKE} strokeWidth="1.5" opacity=".35"/>
          <path d="M120 44 L 120 80" stroke={STROKE} strokeWidth="1.5" opacity=".35"/>
          <path d="M150 50 L 148 80" stroke={STROKE} strokeWidth="1.5" opacity=".35"/>
          <circle cx="120" cy="32" r="8" fill="#F1C24B" stroke={STROKE} strokeWidth="2.5"/>
        </g>
      );
    case 'crown':
      return (
        <g>
          <path d="M70 80 L 80 44 L 102 68 L 120 36 L 138 68 L 160 44 L 170 80 Z"
                fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <circle cx="80" cy="50" r="3.5" fill="#C44A4A" stroke={STROKE} strokeWidth="1.5"/>
          <circle cx="120" cy="46" r="3.5" fill="#C44A4A" stroke={STROKE} strokeWidth="1.5"/>
          <circle cx="160" cy="50" r="3.5" fill="#C44A4A" stroke={STROKE} strokeWidth="1.5"/>
        </g>
      );
    case 'cap':
      return (
        <g>
          <path d="M68 80 C 76 42, 164 42, 172 80 Z"
                fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M40 90 C 60 76, 110 76, 110 84 C 110 92, 60 96, 40 92 Z"
                fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <circle cx="120" cy="54" r="3.5" fill={STROKE}/>
        </g>
      );
    case 'bow':
      return (
        <g transform="translate(52 84)">
          <path d="M0 0 L -18 -11 L -18 11 Z" fill={hatColor} stroke={STROKE} strokeWidth="2.5" strokeLinejoin="round"/>
          <path d="M0 0 L 18 -11 L 18 11 Z" fill={hatColor} stroke={STROKE} strokeWidth="2.5" strokeLinejoin="round"/>
          <ellipse cx="0" cy="0" rx="4.5" ry="6" fill={hatColor} stroke={STROKE} strokeWidth="2.5"/>
        </g>
      );
    case 'flower':
      return (
        <g transform="translate(68 78)">
          <circle cx="0"  cy="-7" r="6" fill="#E68FA5" stroke={STROKE} strokeWidth="2"/>
          <circle cx="-7" cy="3"  r="6" fill="#E68FA5" stroke={STROKE} strokeWidth="2"/>
          <circle cx="7"  cy="3"  r="6" fill="#E68FA5" stroke={STROKE} strokeWidth="2"/>
          <circle cx="-3" cy="-3" r="6" fill="#E68FA5" stroke={STROKE} strokeWidth="2"/>
          <circle cx="0"  cy="-1" r="3.5" fill="#F1C24B" stroke={STROKE} strokeWidth="1.5"/>
        </g>
      );
    case 'top':
      return (
        <g>
          <rect x="56" y="78" width="128" height="7" fill={hatColor} stroke={STROKE} strokeWidth="2.5" rx="2"/>
          <rect x="80" y="22" width="80" height="60" fill={hatColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M80 40 L 160 40" stroke="#C44A4A" strokeWidth="7"/>
        </g>
      );
    default:
      return null;
  }
}

/* ── NECKWEAR ─────────────────────────────────────────────────────── */

function Neckwear({ neckwear, neckColor='#C44A4A' }) {
  switch (neckwear) {
    case 'collar':
      return (
        <g>
          <path d="M70 210 C 100 224, 140 224, 170 210 L 170 218 C 140 232, 100 232, 70 218 Z"
                fill={neckColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <circle cx="120" cy="226" r="6" fill="#E8C552" stroke={STROKE} strokeWidth="2.5"/>
        </g>
      );
    case 'bandana':
      return (
        <g>
          <path d="M106 192 L 134 192 L 130 204 L 110 204 Z"
                fill={neckColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M84 200 L 156 200 L 120 240 Z"
                fill={neckColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <circle cx="100" cy="212" r="1.8" fill="#FFF" opacity=".7"/>
          <circle cx="120" cy="220" r="1.8" fill="#FFF" opacity=".7"/>
          <circle cx="140" cy="212" r="1.8" fill="#FFF" opacity=".7"/>
        </g>
      );
    case 'bowtie':
      return (
        <g>
          <path d="M120 214 L 96 200 L 96 232 Z" fill={neckColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <path d="M120 214 L 144 200 L 144 232 Z" fill={neckColor} stroke={STROKE} strokeWidth="3" strokeLinejoin="round"/>
          <ellipse cx="120" cy="214" rx="6" ry="9" fill={neckColor} stroke={STROKE} strokeWidth="3"/>
        </g>
      );
    case 'none':
    default:
      return null;
  }
}

/* ── BODY hint — small shoulder/chest under the head ─────────────── */

function Body({ coat }) {
  const fill = COATS[coat] || COATS.cream;
  return (
    <path
      d="M78 196 C 88 218, 152 218, 162 196 L 154 244 L 86 244 Z"
      fill={fill} stroke={STROKE} strokeWidth={STROKE_W}
      strokeLinejoin="round" strokeLinecap="round"
    />
  );
}

/* ── PUBLIC: <PetDoodle /> ────────────────────────────────────────── */

function PetDoodle({
  shape='oval',
  ears='floppy',
  eyes='dot',
  nose='bean',
  mouth='smile',
  marking='none',
  coat='cream',
  markColor='white',
  neckwear='none',
  neckColor='#C44A4A',
  hat='none',
  hatColor='#B25223',
  whiskers=false,
  size=240,
  showBody=true,
  className='',
  style={},
}) {
  /* stable id for clip paths (avoid collisions between multiple doodles on a page) */
  const idSuffix = React.useMemo(
    () => Math.random().toString(36).slice(2, 8),
    []
  );

  return (
    <svg
      viewBox="0 0 240 250"
      width={size}
      height={size * (250/240)}
      className={className}
      style={style}
      role="img"
      aria-label="pet doodle"
    >
      {/* a frame group the live preview crops to (mirrors bouquet.jsx's #bouquet-frame) */}
      <g id="pet-frame">
        {showBody && <Body coat={coat} />}
        <Ears ears={ears} coat={coat} />
        <path d={getHeadPath(shape)} {...Fill(COATS[coat] || COATS.cream)} />
        <defs>
          <clipPath id={`head-clip-${idSuffix}`}>
            <path d={getHeadPath(shape)} />
          </clipPath>
        </defs>
        <Markings marking={marking} markColor={markColor} headShape={shape} idSuffix={idSuffix} />
        <Whiskers on={whiskers} />
        <Eyes eyes={eyes} />
        <Nose nose={nose} />
        <Mouth mouth={mouth} nose={nose} />
        <Hat hat={hat} hatColor={hatColor} />
        <Neckwear neckwear={neckwear} neckColor={neckColor} />
      </g>
    </svg>
  );
}

/* ── small util: shift a hex toward black (negative) or white (positive) */
function adjustColor(hex, amt) {
  const n = hex.replace('#','');
  const r = parseInt(n.slice(0,2), 16);
  const g = parseInt(n.slice(2,4), 16);
  const b = parseInt(n.slice(4,6), 16);
  const adj = (c) => {
    if (amt >= 0) return Math.round(c + (255 - c) * amt);
    return Math.round(c * (1 + amt));
  };
  const toHex = (v) => v.toString(16).padStart(2,'0');
  return '#' + toHex(adj(r)) + toHex(adj(g)) + toHex(adj(b));
}

/* ── presets — "starter pets" for the randomizer ─────────────────── */
const PET_PRESETS = [
  { name: 'tabby cat',     shape:'round',  ears:'pointy', eyes:'almond', nose:'tri',  mouth:'cat',    marking:'tabby',  coat:'ginger', markColor:'dark',  neckwear:'collar',  neckColor:'#8E5B1B', whiskers:true  },
  { name: 'tuxedo cat',    shape:'round',  ears:'pointy', eyes:'dot',    nose:'tri',  mouth:'cat',    marking:'tuxedo', coat:'black',  markColor:'white', neckwear:'bowtie',  neckColor:'#C44A4A', whiskers:true  },
  { name: 'golden ret.',   shape:'oval',   ears:'floppy', eyes:'big',    nose:'bean', mouth:'tongue', marking:'none',   coat:'butter', markColor:'white', neckwear:'bandana', neckColor:'#B25223', whiskers:false },
  { name: 'chocolate lab', shape:'oval',   ears:'floppy', eyes:'dot',    nose:'bean', mouth:'tongue', marking:'none',   coat:'chocolate', markColor:'white', neckwear:'collar', neckColor:'#2B6F84', whiskers:false },
  { name: 'frenchie',      shape:'square', ears:'perky',  eyes:'big',    nose:'bean', mouth:'smile',  marking:'mask',   coat:'cream',  markColor:'dark',  neckwear:'collar',  neckColor:'#5B6A4A', whiskers:false },
  { name: 'beagle',        shape:'oval',   ears:'long-floppy', eyes:'dot', nose:'bean', mouth:'smile', marking:'patch', coat:'cream',  markColor:'dark',  neckwear:'collar',  neckColor:'#B25223', whiskers:false },
  { name: 'persian',       shape:'round',  ears:'folded', eyes:'sleepy', nose:'tri',  mouth:'cat',    marking:'none',   coat:'grey',   markColor:'white', neckwear:'bowtie',  neckColor:'#C9A1B4', whiskers:true  },
  { name: 'whippet',       shape:'narrow', ears:'folded', eyes:'almond', nose:'bean', mouth:'smile',  marking:'none',   coat:'cream',  markColor:'dark',  neckwear:'bandana', neckColor:'#8E5B1B', whiskers:false },
  { name: 'spotted pup',   shape:'oval',   ears:'floppy', eyes:'happy',  nose:'bean', mouth:'smile',  marking:'spots',  coat:'white',  markColor:'dark',  neckwear:'collar',  neckColor:'#E5B22A', whiskers:false },
  { name: 'ginger cat',    shape:'round',  ears:'pointy', eyes:'happy',  nose:'tri',  mouth:'cat',    marking:'tabby',  coat:'butter', markColor:'dark',  neckwear:'none',    neckColor:'#000',    whiskers:true  },
];

/* expose for the front-end (window-global, same pattern as bouquet.jsx) */
Object.assign(window, { PetDoodle, COATS, MARKS, PET_PRESETS });
