const { useState, useMemo, useEffect } = React;

const CATEGORIES = ['ITコンサル', '戦略系', '総合系', 'SIer'];
const CAT_CLASS = { 'ITコンサル': 'consul', '戦略系': 'strategy', '総合系': 'sogo', 'SIer': 'sier' };
const catBadgeClass = (c) => CAT_CLASS[c] || 'sier';

function formatMan(n) { return (n ?? 0).toLocaleString(); }

function SunIcon() {
  return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>;
}
function MoonIcon() {
  return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>;
}

function App() {
  const all = window.COMPANIES;

  const [theme, setTheme] = useState(() => localStorage.getItem('dashTheme') || 'light');
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    localStorage.setItem('dashTheme', theme);
  }, [theme]);

  const [categories, setCategories] = useState(new Set(CATEGORIES));
  const [search, setSearch] = useState('');
  const [salaryMin, setSalaryMin] = useState(600);
  const [selected, setSelected] = useState(null);
  const [hovered, setHovered] = useState(null);
  const [tweaksVisible, setTweaksVisible] = useState(false);
  const [showLabels, setShowLabels] = useState('selected');
  const [showAvgLines, setShowAvgLines] = useState(true);

  useEffect(() => {
    const onMsg = (e) => {
      if (!e.data) return;
      if (e.data.type === '__activate_edit_mode') setTweaksVisible(true);
      if (e.data.type === '__deactivate_edit_mode') setTweaksVisible(false);
    };
    window.addEventListener('message', onMsg);
    try { window.parent.postMessage({ type: '__edit_mode_available' }, '*'); } catch (_) {}
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const filtered = useMemo(() => {
    return all.filter(c =>
      categories.has(c.category) &&
      c.salary >= salaryMin &&
      (search === '' || c.name.includes(search) || c.short.includes(search))
    ).sort((a, b) => b.salary - a.salary);
  }, [all, categories, search, salaryMin]);

  const selCompany = selected ? all.find(c => c.name === selected) : null;

  // KPI：カテゴリ別平均 + 最大-最小レンジ
  const kpis = useMemo(() => {
    const top = filtered[0];
    const avg = filtered.length ? Math.round(filtered.reduce((s, c) => s + c.salary, 0) / filtered.length) : 0;
    const totalEmp = filtered.reduce((s, c) => s + c.employees, 0);
    const perCat = {};
    for (const cat of CATEGORIES) {
      const arr = filtered.filter(c => c.category === cat);
      perCat[cat] = arr.length ? Math.round(arr.reduce((s, c) => s + c.salary, 0) / arr.length) : 0;
    }
    const nonZero = Object.values(perCat).filter(v => v > 0);
    const catMax = nonZero.length ? Math.max(...nonZero) : 0;
    const catMin = nonZero.length ? Math.min(...nonZero) : 0;
    const catMaxName = CATEGORIES.find(c => perCat[c] === catMax) || '';
    const catMinName = CATEGORIES.find(c => perCat[c] === catMin) || '';
    return { top, avg, totalEmp, perCat, catMax, catMin, catMaxName, catMinName, count: filtered.length };
  }, [filtered]);

  const toggleCategory = (c) => {
    const next = new Set(categories);
    if (next.has(c)) next.delete(c); else next.add(c);
    if (next.size === 0) return;
    setCategories(next);
  };

  const resetFilters = () => {
    setCategories(new Set(CATEGORIES));
    setSearch('');
    setSalaryMin(600);
    setSelected(null);
  };

  const categoryCount = (cat) => all.filter(c => c.category === cat).length;

  const ageRange = [28, 46];
  const salRange = [600, 2100];
  const tenureRange = [2, 22];

  // Spark 用：全カテゴリの平均推移
  const avgTrend = window.TIMESERIES.years.map((_, i) => {
    const vals = Object.values(window.TIMESERIES.categories).map(arr => arr[i]);
    return Math.round(vals.reduce((a, b) => a + b, 0) / vals.length);
  });

  const filtersAtDefault = categories.size === CATEGORIES.length && search === '' && salaryMin === 600 && !selected;

  return (
    <>
      <header className="app-header">
        <div className="app-header-inner">
          <div className="brand-mark">
            <svg className="logo-mark" width="44" height="44" viewBox="0 0 44 44" aria-label="Salary Topography">
              <rect x="0" y="0" width="44" height="44" rx="3" className="fg"/>
              <g stroke="var(--surface)" strokeWidth="1.2" fill="none" opacity="0.9">
                <path d="M 4 32 Q 14 28 22 30 T 40 28"/>
                <path d="M 4 26 Q 14 20 22 23 T 40 20"/>
                <path d="M 4 20 Q 14 12 22 16 T 40 12"/>
              </g>
              <rect x="20" y="8" width="4" height="28" rx="0.5" className="accent"/>
              <circle cx="22" cy="8" r="2.2" className="accent"/>
            </svg>
            <div>
              <h1>SALARY TOPOGRAPHY <span className="h1-sub">— コンサル × IT 年収地形</span></h1>
              <p className="subtitle">TSE Prime / Non-listed Top Firms · FY2025 · EDINET有価証券報告書＋口コミ推定</p>
            </div>
          </div>
          <div className="header-right">
            <span className="pill"><span className="dot"/>LIVE · EDINET</span>
            <div className="meta-stack">
              対象企業 <strong>{all.length}</strong> 社 / 更新 <strong>2026-04-19</strong><br/>
              本ダッシュボードはデジタル庁ガイドブック準拠
            </div>
            <button className="theme-toggle" onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
              {theme === 'light' ? <MoonIcon/> : <SunIcon/>}
              {theme === 'light' ? 'Dark' : 'Light'}
            </button>
          </div>
        </div>
      </header>

      <div className="app">
        <div className="kpi-row">
          <div className="kpi accent">
            <div className="kpi-top">
              <div className="label">最高年収</div>
              <div className="idx">K01</div>
            </div>
            <div className="value"><span className="num">{kpis.top ? formatMan(kpis.top.salary) : '–'}</span><span className="unit">万円</span></div>
            <div className="footnote">
              <span>{kpis.top ? kpis.top.short : ''}</span>
              <span style={{fontFamily:'var(--font-num)', color:'var(--text-muted)'}}>平均年齢 {kpis.top ? kpis.top.age.toFixed(1) : '–'} 歳</span>
            </div>
          </div>
          <div className="kpi">
            <div className="kpi-top">
              <div className="label">全体平均年収（{kpis.count}社）</div>
              <div className="idx">K02</div>
            </div>
            <div className="value"><span className="num">{formatMan(kpis.avg)}</span><span className="unit">万円</span></div>
            <div className="spark-wrap"><window.Spark values={avgTrend} width={220} height={28} color="var(--chart-primary)"/></div>
            <div className="footnote">
              <span>2020→2025 推移</span>
              <span className="delta">+{avgTrend[avgTrend.length-1] - avgTrend[0]}万円</span>
            </div>
          </div>
          <div className="kpi">
            <div className="kpi-top">
              <div className="label">カテゴリ間 年収差（最高 − 最低）</div>
              <div className="idx">K03</div>
            </div>
            <div className="value"><span className="num">+{formatMan(kpis.catMax - kpis.catMin)}</span><span className="unit">万円</span></div>
            <div className="footnote" style={{ gap: 6, flexWrap: 'wrap' }}>
              <span><span className={`cat-badge ${catBadgeClass(kpis.catMaxName)}`}>{kpis.catMaxName} {formatMan(kpis.catMax)}</span></span>
              <span><span className={`cat-badge ${catBadgeClass(kpis.catMinName)}`}>{kpis.catMinName} {formatMan(kpis.catMin)}</span></span>
            </div>
          </div>
          <div className="kpi">
            <div className="kpi-top">
              <div className="label">対象従業員数（単体合計）</div>
              <div className="idx">K04</div>
            </div>
            <div className="value"><span className="num">{(kpis.totalEmp / 10000).toFixed(1)}</span><span className="unit">万人</span></div>
            <div className="footnote" style={{ gap: 6, flexWrap: 'wrap' }}>
              {CATEGORIES.map(cat => (
                <span key={cat} style={{ fontSize: 10 }}>
                  {cat} {categoryCount(cat)}社
                </span>
              ))}
            </div>
          </div>
        </div>

        <div className="insights">
          <div className="ins-title">KEY OBSERVATIONS</div>
          <div className="insights-grid">
            <div className="ins-item"><div className="num-badge">1</div><div><strong>ベイカレント</strong>は平均年齢 31.2 歳と最若ながら 1,350 万円で上場企業首位。<strong>年齢と年収は比例しない</strong>。</div></div>
            <div className="ins-item"><div className="num-badge">2</div><div><strong>戦略系（MBB）</strong>は 1,850〜2,000 万円と突出。ITコンサル・総合系平均の約 <strong>1.8 倍</strong>に達する。</div></div>
            <div className="ins-item"><div className="num-badge">3</div><div>最大規模の <strong>NTTデータ</strong>（197,777名）は 923 万円。<strong>規模と報酬は相関しない</strong>。</div></div>
            <div className="ins-item"><div className="num-badge">4</div><div>非上場（戦略系 / BIG4 / アクセンチュア等）は <span className="est-inline">推定値</span> で識別表示。開示ベースではなく参考情報。</div></div>
          </div>
        </div>

        <div className="filter-bar">
          <div className="filter-group">
            <span className="filter-label">カテゴリ</span>
            {CATEGORIES.map(c => (
              <button key={c} className={`chip ${categories.has(c) ? 'active' : ''}`} onClick={() => toggleCategory(c)}>
                {c} <span className="count">({categoryCount(c)})</span>
              </button>
            ))}
          </div>
          <div className="filter-group">
            <span className="filter-label">年収下限</span>
            <input type="range" min="600" max="2000" step="50" value={salaryMin} onChange={e => setSalaryMin(+e.target.value)} style={{ width: 120 }} />
            <span className="num" style={{ fontSize: 12, color: 'var(--text-sub)', minWidth: 72 }}>{salaryMin}万円〜</span>
          </div>
          <div className="filter-group">
            <span className="filter-label">検索</span>
            <input className="search-input" placeholder="企業名で絞り込み" value={search} onChange={e => setSearch(e.target.value)} />
          </div>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 12, alignItems: 'center' }}>
            <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>該当 <span className="num" style={{ color: 'var(--text)', fontWeight: 600 }}>{filtered.length}</span> / {all.length} 社</span>
            <button className="filter-reset" onClick={resetFilters} disabled={filtersAtDefault}>リセット</button>
          </div>
        </div>

        <div className="section">
          <div className="section-head">
            <div className="title-wrap"><span className="title-accent"/><h2>年収ランキング — {filtered.length}社</h2></div>
            <span className="note">クリックで選択 ／ 全チャートと連動</span>
          </div>
          <div className="section-body">
            <window.RankingChart companies={filtered} highlightName={hovered} selectedName={selected} onSelect={(n) => setSelected(selected === n ? null : n)} />
          </div>
        </div>

        <div className="grid-main">
          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>年齢 × 年収</h2></div>
              <span className="note">単体ベース平均</span>
            </div>
            <div className="section-body">
              <window.ScatterChart companies={filtered} xKey="age" yKey="salary" xLabel="平均年齢（歳）" yLabel="平均年収（万円）" xRange={ageRange} yRange={salRange} highlightName={hovered} selectedName={selected} onSelect={(n) => setSelected(selected === n ? null : n)} labelAll={showLabels === 'all'} showAvgLines={showAvgLines} showQuadrants={true}/>
            </div>
          </div>

          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>カテゴリ別分布</h2></div>
              <span className="note">中央値・平均・四分位範囲</span>
            </div>
            <div className="section-body">
              <window.CategoryCompare companies={filtered} />
            </div>
          </div>

          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>勤続年数 × 年収</h2></div>
              <span className="note">長く働くほど高い？</span>
            </div>
            <div className="section-body">
              <window.ScatterChart companies={filtered} xKey="tenure" yKey="salary" xLabel="平均勤続年数（年）" yLabel="平均年収（万円）" xRange={tenureRange} yRange={salRange} highlightName={hovered} selectedName={selected} onSelect={(n) => setSelected(selected === n ? null : n)} labelAll={showLabels === 'all'} showAvgLines={showAvgLines} showQuadrants={false}/>
            </div>
          </div>

          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>年収帯ヒストグラム</h2></div>
              <span className="note">200万円刻み・カテゴリ積み上げ</span>
            </div>
            <div className="section-body">
              <window.HistogramChart companies={filtered} highlightName={hovered || selected} />
            </div>
          </div>

          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>カテゴリ別 平均年収 推移</h2></div>
              <span className="note">2020–2025</span>
            </div>
            <div className="section-body">
              <window.TimeSeriesChart data={window.TIMESERIES} />
            </div>
          </div>

          <div className="section">
            <div className="section-head">
              <div className="title-wrap"><span className="title-accent"/><h2>企業詳細</h2></div>
              <span className="note">{selCompany ? selCompany.short : '未選択'}</span>
            </div>
            <div className="section-body">
              {selCompany ? (
                <div className="detail-card">
                  <div className="d-head">
                    <div>
                      <div className="d-name">{selCompany.name}</div>
                      <span className={`cat-badge ${catBadgeClass(selCompany.category)}`}>{selCompany.category}</span>
                      {selCompany.estimated && <span className="est-inline" style={{ marginLeft: 6 }}>推定値</span>}
                    </div>
                    <button className="d-close" onClick={() => setSelected(null)}>×</button>
                  </div>
                  <div className="d-grid">
                    <div className="d-row"><span className="k">平均年収</span><span className="v">{formatMan(selCompany.salary)} 万円</span></div>
                    <div className="d-row"><span className="k">平均年齢</span><span className="v">{selCompany.age.toFixed(1)} 歳</span></div>
                    <div className="d-row"><span className="k">平均勤続</span><span className="v">{selCompany.tenure.toFixed(1)} 年</span></div>
                    <div className="d-row"><span className="k">従業員数</span><span className="v">{formatMan(selCompany.employees)} 名</span></div>
                    <div className="d-row"><span className="k">開示区分</span><span className="v" style={{ fontFamily: 'var(--font-ja)' }}>{selCompany.listed ? '上場' : '非上場'}</span></div>
                    <div className="d-row"><span className="k">業界内順位</span><span className="v">{
                      (() => {
                        const sameCat = all.filter(c => c.category === selCompany.category).sort((a, b) => b.salary - a.salary);
                        return `${sameCat.findIndex(c => c.name === selCompany.name) + 1} / ${sameCat.length}`;
                      })()
                    }</span></div>
                  </div>
                  <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px dotted var(--border-soft)', fontSize: 11, color: 'var(--text-label)' }}>
                    同カテゴリ平均との差:
                    <span style={{ marginLeft: 8, fontWeight: 600, fontFamily: 'var(--font-num)' }}>
                      {(() => {
                        const catAvg = kpis.perCat[selCompany.category] || 0;
                        const diff = selCompany.salary - catAvg;
                        return <span style={{ color: diff >= 0 ? 'var(--positive)' : 'var(--negative)' }}>{diff >= 0 ? '+' : ''}{formatMan(diff)} 万円</span>;
                      })()}
                    </span>
                  </div>
                </div>
              ) : (
                <div className="detail-empty">
                  <span className="big">+</span>
                  企業を選択すると詳細がここに表示されます<br/>
                  <span style={{fontSize:10}}>ランキング・チャート上の点をクリック</span>
                </div>
              )}
            </div>
          </div>
        </div>

        <footer className="app-footer">
          <div>
            <h4>データ定義</h4>
            平均年収：有価証券報告書「従業員の状況」の平均年間給与（税込・賞与含む）。提出会社（単体）ベース。<br/>
            平均年齢・勤続年数：同報告書記載の提出時点の平均。<br/>
            従業員数：単体ベース。
          </div>
          <div>
            <h4>データ出典</h4>
            <a href="https://disclosure2.edinet-fsa.go.jp/" target="_blank" rel="noopener noreferrer">EDINET（金融庁）</a> 有価証券報告書<br/>
            非上場（<span className="est-inline">推定値</span>）は各社公表情報・転職市場データをもとにした参考推定値。
          </div>
          <div>
            <h4>免責事項</h4>
            本ダッシュボードの情報は参考目的のみ。投資判断・採用判断の根拠として利用しないでください。<br/>
            デジタル庁「ダッシュボードデザインの実践ガイドブック」を参考に制作。
          </div>
        </footer>

        <div className={`tweaks ${tweaksVisible ? 'visible' : ''}`}>
          <div className="tweaks-head">
            <h3>TWEAKS</h3>
            <button className="d-close" onClick={() => setTweaksVisible(false)}>×</button>
          </div>
          <div className="tweaks-body">
            <div className="tweaks-row">
              <label>テーマ</label>
              <div className="seg">
                <button className={theme === 'light' ? 'on' : ''} onClick={() => setTheme('light')}>Light</button>
                <button className={theme === 'dark' ? 'on' : ''} onClick={() => setTheme('dark')}>Dark</button>
              </div>
            </div>
            <div className="tweaks-row">
              <label>散布図のラベル表示</label>
              <div className="seg">
                <button className={showLabels === 'selected' ? 'on' : ''} onClick={() => setShowLabels('selected')}>選択のみ</button>
                <button className={showLabels === 'all' ? 'on' : ''} onClick={() => setShowLabels('all')}>すべて</button>
              </div>
            </div>
            <div className="tweaks-row">
              <label>平均線の表示</label>
              <div className="seg">
                <button className={showAvgLines ? 'on' : ''} onClick={() => setShowAvgLines(true)}>ON</button>
                <button className={!showAvgLines ? 'on' : ''} onClick={() => setShowAvgLines(false)}>OFF</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}

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