// 自転車診断ツール — メインアプリ
// Start → Q1...Q7 → Result の状態機械。
// 結果URLはハッシュにエンコードしてシェア可能にする。

const { useState, useEffect, useMemo, useCallback } = React;
const { CATEGORIES, QUESTIONS, score, recommendBikes } = window.BikeData;

// ========================================================
// URLハッシュとのシリアライズ
// 形式: #/result?q1=city,commute&q2=weekend&q3=comfort&...
// ========================================================
function encodeAnswers(answers) {
  const parts = [];
  for (const q of QUESTIONS) {
    const a = answers[q.id];
    if (!a) continue;
    const v = Array.isArray(a) ? a.join(',') : a;
    parts.push(`${q.id}=${encodeURIComponent(v)}`);
  }
  return parts.join('&');
}
function decodeAnswers(str) {
  const result = {};
  if (!str) return result;
  for (const p of str.split('&')) {
    const [k, v] = p.split('=');
    if (!k || v === undefined) continue;
    const decoded = decodeURIComponent(v);
    const q = QUESTIONS.find(x => x.id === k);
    if (!q) continue;
    if (q.type === 'multi') result[k] = decoded.split(',').filter(Boolean);
    else result[k] = decoded;
  }
  return result;
}
function parseHash() {
  const h = window.location.hash.replace(/^#\/?/, '');
  if (!h) return { step: 'start', answers: {} };
  if (h.startsWith('result')) {
    const qs = h.includes('?') ? h.split('?')[1] : '';
    return { step: 'result', answers: decodeAnswers(qs) };
  }
  const m = h.match(/^q\/(\d+)/);
  if (m) {
    const idx = Math.max(1, Math.min(QUESTIONS.length, +m[1])) - 1;
    return { step: 'q', qIdx: idx, answers: {} };
  }
  return { step: 'start', answers: {} };
}

// ========================================================
// UI: 共通ヘッダー
// ========================================================
function AppHeader({ step, qIdx }) {
  let meta = 'v1.0';
  if (step === 'q')      meta = `Q${qIdx + 1} / ${QUESTIONS.length}`;
  if (step === 'result') meta = 'RESULT';
  if (step === 'start')  meta = 'START';
  return (
    <header className="app-header">
      <div className="app-brand">
        <div className="app-mark">G</div>
        <div className="app-brand-text">
          BIKE FINDER
          <small>SPORTS BIKE 診断</small>
        </div>
      </div>
      <div className="app-meta">{meta}</div>
    </header>
  );
}

// ========================================================
// Start screen
// ========================================================
function StartScreen({ onStart }) {
  return (
    <section className="start fade-in">
      <span className="tag accent">DIAGNOSIS / 7問</span>
      <h1 className="h1">
        あなたに<br />
        向いてる1台、<em>一緒に探します。</em>
      </h1>
      <p className="lede">
        街乗り？ ロングライド？ 山も走りたい？<br />
        かんたんな7問の質問にタップで答えると、<br />
        おすすめの車種と特徴を表示します。
      </p>
      <p className="start-note">
        <b>SPECIALIZED</b> のラインナップから<br />
        ピックアップしてご紹介します。
      </p>

      <div className="start-photo">
        <img src="images/hero.png" alt="BIKE FINDER" />
      </div>

      <div className="start-meta">
        <div className="start-meta-item">
          <div className="start-meta-key">所要時間</div>
          <div className="start-meta-val">約 1 分</div>
        </div>
        <div className="start-meta-item">
          <div className="start-meta-key">診断対象</div>
          <div className="start-meta-val">5カテゴリ</div>
        </div>
      </div>

      <div className="start-bottom">
        <button className="btn btn-primary" onClick={onStart}>▶ 診断スタート</button>
        <p className="start-fineprint">
          ※ 結果はあくまで目安です。<br />
          気になる1台は店舗でも相談できます。
        </p>
      </div>
    </section>
  );
}

// ========================================================
// Question screen
// ========================================================
function QuestionScreen({ qIdx, answers, onAnswer, onBack, onNext }) {
  const q = QUESTIONS[qIdx];
  const total = q.total;
  const current = answers[q.id];

  // Q1 (multi w/ conflicts): 選択中のkeyから「競合する」keyを自動でdisableにする
  const disabledKeys = useMemo(() => {
    if (q.type !== 'multi') return new Set();
    const sel = current || [];
    const dis = new Set();
    for (const sk of sel) {
      const o = q.options.find(x => x.k === sk);
      if (o && o.conflicts) for (const c of o.conflicts) dis.add(c);
    }
    // 逆方向：自分自身が誰かのconflictsに入っているなら、その誰かが選ばれていれば disabled
    // (上のループで対応済みなので追加処理不要)
    return dis;
  }, [q, current]);

  const isOptSelected = (k) => {
    if (q.type === 'multi') return (current || []).includes(k);
    return current === k;
  };

  const handlePick = (k) => {
    if (q.type === 'multi') {
      const cur = current || [];
      if (cur.includes(k)) {
        onAnswer(q.id, cur.filter(x => x !== k));
      } else {
        if (disabledKeys.has(k)) return;
        if (q.max && cur.length >= q.max) return;
        onAnswer(q.id, [...cur, k]);
      }
    } else {
      onAnswer(q.id, k);
    }
  };

  const canNext = q.type === 'multi'
    ? (current || []).length > 0
    : !!current;

  // 競合グレーアウト発生中の通知
  const hasConflicts = disabledKeys.size > 0;

  return (
    <section className="q fade-in" key={qIdx}>
      <div className="q-bar">
        <span className="q-bar-text">Q{qIdx + 1} / {total}</span>
        <div className="q-bar-track">
          {Array.from({ length: total }).map((_, i) =>
            <span key={i} className={i <= qIdx ? 'on' : ''} />)}
        </div>
      </div>

      <h2 className="h1 q-title">{q.title}</h2>
      <p className="q-hint">
        {q.type === 'multi'
          ? <>あてはまるものを <b>最大{q.max}つ</b> まで。</>
          : q.hint}
      </p>

      <div className="q-options">
        {q.options.map(o => {
          const sel = isOptSelected(o.k);
          const dis = disabledKeys.has(o.k) && !sel;
          return (
            <button
              key={o.k}
              className={`opt${sel ? ' selected' : ''}${dis ? ' disabled' : ''}`}
              onClick={() => handlePick(o.k)}
              disabled={dis}>
              <span className={`opt-check${q.type === 'single' ? ' radio' : ''}`}>
                {sel ? (q.type === 'multi' ? '✓' : '●') : ''}
              </span>
              <span className="opt-text">
                <span className="opt-label">{o.l}</span>
                {o.sub && <span className="opt-sub">{o.sub}</span>}
              </span>
            </button>
          );
        })}
      </div>

      {hasConflicts && (
        <p className="q-warn">同時に選びにくい項目はグレーになります。</p>
      )}

      <div className="q-foot">
        <button className="btn btn-ghost" onClick={onBack}>戻る</button>
        <button
          className="btn btn-primary"
          onClick={onNext}
          disabled={!canNext}
          style={{ opacity: canNext ? 1 : .4 }}>
          {qIdx + 1 === total ? '結果を見る →' : '次へ →'}
        </button>
      </div>
    </section>
  );
}

// ========================================================
// Result screen
// ========================================================
function CategoryDetail({ category, bikes, rank }) {
  const isTop = rank === 1;
  return (
    <>
      <div className="result-head">
        <div className="result-eyebrow">
          {isTop ? 'BEST MATCH / 結果' : 'SECOND PICK / 次点'}
        </div>
        <h1 className="result-title">{category.name}</h1>
        <div className="result-en">{category.en}</div>
        <p className="result-tagline">{category.tagline}</p>
      </div>

      {category.image && (
        <div className="result-photo">
          <img src={category.image} alt={category.name} />
        </div>
      )}

      <div className="result-section">
        <p className="result-summary">{category.summary}</p>

        <dl className="miniSpec">
          <dt>得意分野</dt><dd>{category.goodFor.join(' / ')}</dd>
          <dt>価格目安</dt><dd>{category.priceRange}</dd>
        </dl>

        <div className="prosCons">
          <div className="pcCol merit">
            <div className="h3">◎ メリット</div>
            <ul>{category.pros.map((p, i) => <li key={i}>{p}</li>)}</ul>
          </div>
          <div className="pcCol care">
            <div className="h3">△ 注意点</div>
            <ul>{category.cons.map((p, i) => <li key={i}>{p}</li>)}</ul>
          </div>
        </div>

        <div className="priceBlock">
          <div className="priceBlock-key">PRICE / 初心者目安</div>
          <div className="priceBlock-val">{category.priceRange}</div>
          <div className="priceBlock-sub">※ Specialized {category.name} ラインナップより</div>
        </div>
      </div>

      <div className="result-section">
        <div className="h3" style={{ marginBottom: 4 }}>RECOMMENDED MODEL</div>
        <p className="lede" style={{ fontSize: 12, marginBottom: 4 }}>
          {category.name}のなかから、初心者にやさしい1台をピックアップ。
        </p>
        {bikes.map((b) => (
          <div className="modelCard" key={b.key}>
            <div className="modelCard-photo">
              <img src={b.image} alt={b.name} loading="lazy"
                onError={(e) => { e.currentTarget.style.display = 'none'; }} />
              {bikes.length > 1 && (
                <span className="modelCard-stamp">{b.grade}</span>
              )}
            </div>
            <div className="modelCard-body">
              <div className="modelCard-tag">{b.grade} · {b.tag}</div>
              <h3 className="modelCard-name">{b.name}</h3>
              <div className="modelCard-price">{b.price}</div>
              <a className="btn btn-primary" href={b.url} target="_blank" rel="noopener">
                商品ページを見る →
              </a>
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

function ResultScreen({ answers, onRestart }) {
  const { ranked } = useMemo(() => score(answers), [answers]);
  const top    = CATEGORIES[ranked[0].key];
  const second = CATEGORIES[ranked[1].key];
  const topBikes    = useMemo(() => recommendBikes(top.key, answers), [top.key, answers]);
  const secondBikes = useMemo(() => recommendBikes(second.key, answers), [second.key, answers]);

  const [toast, setToast] = useState(null);
  const copyURL = () => {
    const url = location.origin + location.pathname + '#/result?' + encodeAnswers(answers);
    navigator.clipboard.writeText(url).then(
      () => { setToast('URLをコピーしました'); setTimeout(() => setToast(null), 2000); },
      () => { setToast('コピーできませんでした'); setTimeout(() => setToast(null), 2000); },
    );
  };

  return (
    <section className="result fade-in">
      <CategoryDetail category={top} bikes={topBikes} rank={1} />

      <div className="result-divider" />

      <CategoryDetail category={second} bikes={secondBikes} rank={2} />

      <div className="result-foot">
        <button className="btn btn-ghost" onClick={onRestart}>↺ もう一度</button>
        <button className="btn" onClick={copyURL}>結果URLコピー</button>
      </div>

      <p className="result-fineprint">
        ※ 結果はあくまで目安です。実際の1台選びは、お気軽に店舗スタッフへもご相談ください。
      </p>

      <footer className="app-footer">© GP BIKE GUIDE</footer>

      {toast && <div className="toast">{toast}</div>}
    </section>
  );
}

// ========================================================
// Root
// ========================================================
function App() {
  // 初期状態をURLハッシュから復元
  const initial = useMemo(() => parseHash(), []);
  const [step, setStep]       = useState(initial.step); // 'start' | 'q' | 'result'
  const [qIdx, setQIdx]       = useState(initial.qIdx || 0);
  const [answers, setAnswers] = useState(initial.answers || {});

  // ハッシュ変化（戻る/進む）でも追従
  useEffect(() => {
    const onHash = () => {
      const s = parseHash();
      setStep(s.step);
      setQIdx(s.qIdx || 0);
      if (s.step === 'result') setAnswers(s.answers || {});
    };
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  // ハッシュへの反映（pushState 風にしてブラウザバックを効かせる）
  const updateHash = useCallback((newHash) => {
    if (window.location.hash !== newHash) {
      history.pushState(null, '', newHash);
    }
  }, []);

  const start = () => {
    setStep('q'); setQIdx(0);
    updateHash('#/q/1');
    window.scrollTo({ top: 0 });
  };
  const answer = (qid, val) => setAnswers(a => ({ ...a, [qid]: val }));
  const next = () => {
    if (qIdx + 1 >= QUESTIONS.length) {
      setStep('result');
      updateHash('#/result?' + encodeAnswers(answers));
      window.scrollTo({ top: 0 });
    } else {
      setQIdx(i => i + 1);
      updateHash(`#/q/${qIdx + 2}`);
      window.scrollTo({ top: 0 });
    }
  };
  const back = () => {
    if (qIdx === 0) {
      setStep('start');
      updateHash('');
    } else {
      setQIdx(i => i - 1);
      updateHash(`#/q/${qIdx}`);
    }
    window.scrollTo({ top: 0 });
  };
  const restart = () => {
    setStep('start');
    setQIdx(0);
    setAnswers({});
    updateHash('');
    window.scrollTo({ top: 0 });
  };

  return (
    <div className="app">
      <main className="app-main">
        {step === 'start' && <StartScreen onStart={start} />}
        {step === 'q' && (
          <QuestionScreen
            qIdx={qIdx}
            answers={answers}
            onAnswer={answer}
            onBack={back}
            onNext={next} />
        )}
        {step === 'result' && (
          <ResultScreen answers={answers} onRestart={restart} />
        )}
      </main>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
