// 脳内ビジュアライザー
// 頭部シルエットの中に診断漢字を散布する SVG コンポーネント
// 配置数: count = max(1, round(ratio/5))  (仕様書 §9.3)

(function () {
  const { useMemo } = React;

  // ---- 簡易疑似乱数 (mulberry32) — シード固定で同じ結果なら同じ配置 ----
  function makeRng(seed) {
    let a = seed >>> 0;
    return function () {
      a |= 0;
      a = (a + 0x6d2b79f5) | 0;
      let t = a;
      t = Math.imul(t ^ (t >>> 15), t | 1);
      t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    };
  }
  function hashString(s) {
    let h = 2166136261 >>> 0;
    for (let i = 0; i < s.length; i++) {
      h ^= s.charCodeAt(i);
      h = Math.imul(h, 16777619);
    }
    return h >>> 0;
  }

  // 脳内エリア = 楕円。中心 (cx,cy), 半径 (rx, ry)
  // 役割ごとの正規化半径レンジ
  const ROLE_RANGES = {
    main: [0.0, 0.42],
    sub: [0.35, 0.78],
    rare: [0.78, 1.02],
  };
  const ROLE_FONT = {
    main: [40, 64],
    sub: [26, 44],
    rare: [18, 28],
  };

  // 既存配置と最小距離をなるべく取って散布
  function placeWords(brainWords, seedKey, area) {
    const rng = makeRng(hashString(seedKey || "x"));
    const { cx, cy, rx, ry } = area;
    const placed = [];

    // 配置順: rare → sub → main の順で置くと main が見えやすい
    const order = brainWords
      .map((b, i) => ({ ...b, i }))
      .sort((a, b) => {
        const w = { rare: 0, sub: 1, main: 2 };
        return w[a.role] - w[b.role];
      });

    for (const b of order) {
      const count = Math.max(1, Math.round(b.ratio / 5));
      const [rmin, rmax] = ROLE_RANGES[b.role];
      const [fmin, fmax] = ROLE_FONT[b.role];

      for (let k = 0; k < count; k++) {
        let best = null;
        // 候補から最も既存と離れている点を選ぶ
        const tries = 18;
        for (let t = 0; t < tries; t++) {
          const r = rmin + (rmax - rmin) * Math.sqrt(rng());
          const theta = rng() * Math.PI * 2;
          // やや上寄せ (脳は頭部上半分)
          const x = cx + Math.cos(theta) * r * rx;
          const y = cy + Math.sin(theta) * r * ry * 0.95 - ry * 0.05;
          let minDist = Infinity;
          for (const p of placed) {
            const dx = p.x - x,
              dy = p.y - y;
            const d = Math.hypot(dx, dy);
            if (d < minDist) minDist = d;
          }
          if (!best || minDist > best.minDist) {
            best = { x, y, minDist };
          }
        }
        const size = fmin + (fmax - fmin) * rng();
        const rot = (rng() - 0.5) * 22; // -11°〜+11°
        const opacity = 0.78 + rng() * 0.22;
        placed.push({
          x: best.x,
          y: best.y,
          word: b.word,
          role: b.role,
          size,
          rot,
          opacity,
        });
      }
    }
    return placed;
  }

  function BrainView({ brainWords, seedKey, accentColor = "#ff2e88" }) {
    // viewBox 480 x 520
    const W = 480,
      H = 520;
    // 頭部シルエット（やや横顔寄りの正面シルエット）
    // 脳エリア: 上半分の楕円
    const area = { cx: 240, cy: 230, rx: 178, ry: 165 };

    const placed = useMemo(
      () => placeWords(brainWords, seedKey, area),
      [brainWords, seedKey]
    );

    return (
      <svg
        viewBox={`0 0 ${W} ${H}`}
        width="100%"
        height="100%"
        style={{ display: "block" }}
        aria-label="脳内メーカー結果"
      >
        <defs>
          <clipPath id="brainClip">
            <ellipse cx={area.cx} cy={area.cy} rx={area.rx} ry={area.ry} />
          </clipPath>
          <filter id="paper" x="0" y="0" width="100%" height="100%">
            <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="3" />
            <feColorMatrix values="0 0 0 0 0.85  0 0 0 0 0.78  0 0 0 0 0.68  0 0 0 0.06 0" />
            <feComposite in2="SourceGraphic" operator="in" />
          </filter>
        </defs>

        {/* 影 */}
        <ellipse cx={240} cy={502} rx={140} ry={10} fill="#1a1a2e" opacity="0.12" />

        {/* 首・肩 */}
        <path
          d="M 168 430 Q 168 470 130 488 L 350 488 Q 312 470 312 430 Z"
          fill="#F4D9C2"
          stroke="#1f2138"
          strokeWidth="3"
          strokeLinejoin="round"
        />
        {/* 服の襟 */}
        <path
          d="M 130 488 Q 240 472 350 488 L 360 510 L 120 510 Z"
          fill={accentColor}
          stroke="#1f2138"
          strokeWidth="3"
          strokeLinejoin="round"
        />

        {/* 頭 (オーバル) */}
        <ellipse
          cx={240}
          cy={232}
          rx={186}
          ry={196}
          fill="#FBE6D2"
          stroke="#1f2138"
          strokeWidth="3.5"
        />

        {/* 髪の毛トップ（軽く） */}
        <path
          d="M 78 200 Q 105 80 240 60 Q 380 80 402 205 Q 372 150 320 142 Q 268 168 240 150 Q 210 170 162 150 Q 110 158 78 200 Z"
          fill="#2b2540"
          stroke="#1f2138"
          strokeWidth="3"
          strokeLinejoin="round"
          opacity="0.92"
        />

        {/* 耳 */}
        <ellipse cx={56} cy={260} rx={16} ry={26} fill="#F4D9C2" stroke="#1f2138" strokeWidth="3" />
        <ellipse cx={424} cy={260} rx={16} ry={26} fill="#F4D9C2" stroke="#1f2138" strokeWidth="3" />

        {/* 目 */}
        <g>
          <ellipse cx={188} cy={340} rx={9} ry={11} fill="#1f2138" />
          <ellipse cx={292} cy={340} rx={9} ry={11} fill="#1f2138" />
          <circle cx={191} cy={336} r={2.4} fill="#fff" />
          <circle cx={295} cy={336} r={2.4} fill="#fff" />
          {/* まつげ風 */}
          <path d="M 176 326 Q 188 318 200 326" stroke="#1f2138" strokeWidth="2.5" fill="none" strokeLinecap="round" />
          <path d="M 280 326 Q 292 318 304 326" stroke="#1f2138" strokeWidth="2.5" fill="none" strokeLinecap="round" />
        </g>

        {/* 鼻 */}
        <path d="M 240 360 Q 232 380 240 388 Q 246 386 250 384" stroke="#1f2138" strokeWidth="2.5" fill="none" strokeLinecap="round" />

        {/* 口 */}
        <path d="M 218 408 Q 240 422 262 408" stroke="#1f2138" strokeWidth="3" fill="none" strokeLinecap="round" />

        {/* ほっぺ */}
        <circle cx={170} cy={384} r={12} fill="#F5B6B8" opacity="0.55" />
        <circle cx={310} cy={384} r={12} fill="#F5B6B8" opacity="0.55" />

        {/* === 脳内ウィンドウ（楕円のクリップ + 半透明背景） === */}
        <ellipse
          cx={area.cx}
          cy={area.cy}
          rx={area.rx}
          ry={area.ry}
          fill="#FFF9F1"
          stroke="#1f2138"
          strokeWidth="2.5"
          strokeDasharray="2 6"
        />

        {/* 漢字 (クリップ内) */}
        <g clipPath="url(#brainClip)">
          {/* ノイズ風背景 */}
          <rect x={area.cx - area.rx} y={area.cy - area.ry} width={area.rx * 2} height={area.ry * 2}
                fill="url(#paper)" opacity="0.4" />
          {placed.map((p, idx) => (
            <text
              key={idx}
              x={p.x}
              y={p.y}
              fontSize={p.size}
              fontFamily="'Dela Gothic One', 'M PLUS Rounded 1c', sans-serif"
              fontWeight={p.role === "main" ? 900 : p.role === "sub" ? 700 : 600}
              fill={p.role === "main" ? accentColor : "#1f2138"}
              opacity={p.opacity}
              textAnchor="middle"
              dominantBaseline="central"
              transform={`rotate(${p.rot} ${p.x} ${p.y})`}
              style={{ fontFeatureSettings: "'palt'" }}
            >
              {p.word}
            </text>
          ))}
        </g>

        {/* 脳内ラベル（脳内エリア外、上端） */}
        <g>
          <rect x={196} y={62} width={88} height={22} rx={11} fill="#1f2138" />
          <text x={240} y={78} textAnchor="middle" fill="#FFF9F1" fontSize="13" fontFamily="'Noto Sans JP', sans-serif" fontWeight="700" letterSpacing="2">
            脳内
          </text>
        </g>
      </svg>
    );
  }

  window.BrainView = BrainView;
})();
