/* demand-trend-panel.jsx — 적재된 네이버 데이터랩 수요 시계열 화면 (P1 프론트)
 *
 *  ★엔진·API 만 만들고 화면을 안 만드는 실패를 반복하지 않는다. 적재층(cron) ·
 *    조회층(/api/demand) · 화면(이 파일)이 한 세트다.
 *
 *  화면이 반드시 같이 내는 것(하나라도 빠지면 미완):
 *   ① ratio 가 **창 내 최대=100 상대지수**라는 사실 — 절대 검색량이 아니다
 *   ② 지금 보고 있는 **창(window)** 이 무엇인지, 창이 섞였으면 그 경고
 *   ③ 마지막 구간은 **잠정값**(재호출 시 움직임을 실측) — 확정처럼 그리지 않는다
 *   ④ **관측 공백**(uncollected) — 0 과 "확인 못 함"을 다르게 그린다
 *   ⑤ tier · n
 */
(function () {
  var useState = React.useState, useEffect = React.useEffect, useCallback = React.useCallback;

  var MONO = 'var(--font-mono)';
  function Badge({ tone, children }) {
    var c = { amber: ['#3a2a12', '#f0b46a'], cyan: ['#11303a', '#5fc7e0'], plant: ['#13301f', '#5fcf94'],
              mute: ['#23262d', '#9aa0aa'], red: ['#3a1b1b', '#e08a8a'] }[tone || 'mute'];
    return <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, padding: '2px 8px', borderRadius: 4,
      fontSize: 10.5, fontFamily: MONO, fontWeight: 600, background: c[0], color: c[1], whiteSpace: 'nowrap' }}>{children}</span>;
  }
  function Card({ children, style }) {
    return <div style={Object.assign({ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 9, padding: '15px 17px' }, style)}>{children}</div>;
  }
  function H4({ children }) {
    return <h4 style={{ margin: '0 0 10px', fontSize: 11.5, letterSpacing: '0.05em', color: 'var(--ink-400)', textTransform: 'uppercase', fontFamily: MONO }}>{children}</h4>;
  }

  function DemandTrendPanel() {
    var [data, setData] = useState(null);
    var [loading, setLoading] = useState(false);
    var [err, setErr] = useState('');
    var [group, setGroup] = useState('');
    // ★단위(month/date)는 창이 달라 **다른 계열**이다. 키에 넣지 않으면 두 계열이 한 그래프에
    //   섞여 그려진다 — 축이 조용히 어긋나는 이 프로젝트의 대표 함정이다.
    var [unit, setUnit] = useState('');

    var load = useCallback(function (groupArg, unitArg) {
      var g = groupArg === undefined ? group : groupArg;
      var u = unitArg === undefined ? unit : unitArg;
      setLoading(true); setErr('');
      var qs = [];
      if (g) qs.push('group=' + encodeURIComponent(g));
      if (u) qs.push('unit=' + encodeURIComponent(u));
      fetch('/api/demand' + (qs.length ? '?' + qs.join('&') : ''), { cache: 'no-store' })
        .then(function (r) { return r.json(); })
        .then(function (j) {
          if (!j || j.ok === false) { setErr((j && (j.error || j.verdict)) || '조회 실패'); setData(null); }
          else setData(j);
        })
        // 네트워크 실패를 "데이터 없음"으로 보이게 하지 않는다.
        .catch(function (e) { setErr('네트워크 실패: ' + e.message); setData(null); })
        .then(function () { setLoading(false); });
    }, [group, unit]);

    useEffect(function () { load(); }, []); // eslint-disable-line

    var trend = (data && data.trend) || [];
    var insight = (data && data.insight) || [];
    var uncollected = (data && data.uncollected) || [];
    var meta = (data && data._meta) || {};

    // ★키워드·단위 목록은 **catalog**(DB 가 실제로 가진 것)에서만 만든다.
    //   지금 화면에 로드된 trend 로 만들면, 단위를 좁혀 받는 순간 다른 단위가 목록에서 사라진다.
    var catalog = (data && data.catalog) || [];
    var groups = [];
    catalog.forEach(function (c) { if (groups.indexOf(c.group_title) < 0) groups.push(c.group_title); });
    if (!groups.length) trend.forEach(function (t) { if (groups.indexOf(t.group_title) < 0) groups.push(t.group_title); });
    var shown = group || groups[0] || '';
    var unitsForShown = catalog.filter(function (c) { return c.group_title === shown; })
                               .map(function (c) { return c.time_unit; });
    var shownUnit = (unit && unitsForShown.indexOf(unit) >= 0) ? unit : (unitsForShown[0] || '');

    var series = trend.filter(function (t) {
      return t.group_title === shown && (!shownUnit || t.time_unit === shownUnit);
    });
    // ★일 단위는 400점까지 온다. 막대를 400개 그리면 읽을 수 없다 —
    //   최근 구간만 그리되 **몇 점을 접었는지 반드시 적는다**(조용한 절단 금지).
    var MAX_BARS = 90;
    var folded = Math.max(0, series.length - MAX_BARS);
    var bars = folded ? series.slice(-MAX_BARS) : series;
    var maxRatio = bars.reduce(function (m, t) { return Math.max(m, Number(t.ratio) || 0); }, 0) || 100;

    return (
      <div style={{ display: 'grid', gap: 14 }}>
        <Card>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginBottom: 8 }}>
            <h3 style={{ margin: 0, fontSize: 15 }}>수요 시계열 — 네이버 데이터랩 적재분</h3>
            <Badge tone="cyan">tier {meta.tier || 'B'} · 관측</Badge>
            <Badge tone="mute">n={meta.n || 0}</Badge>
            <button onClick={load} disabled={loading}
              style={{ marginLeft: 'auto', background: 'transparent', border: '1px solid var(--border)', color: 'var(--ink-400)', borderRadius: 6, padding: '4px 10px', cursor: 'pointer', fontSize: 12 }}>
              {loading ? '조회 중…' : '새로고침'}
            </button>
          </div>

          {/* ① 무엇을 보고 있는지 — 숫자보다 먼저 */}
          <p style={{ margin: '0 0 10px', fontSize: 11.5, color: 'var(--ink-400)', lineHeight: 1.6 }}>
            {meta.note || 'ratio 는 창 내 최대=100 상대지수다 — 절대 검색량이 아니다.'}
          </p>

          {/* ② 창 */}
          {meta.windows && meta.windows.length > 0 && (
            <div style={{ fontSize: 11, fontFamily: MONO, color: 'var(--ink-400)', marginBottom: 8 }}>
              창 {meta.windows.join(' , ')}
            </div>
          )}
          {meta.truncated && (
            <div style={{ background: '#3a2a12', color: '#f0b46a', borderRadius: 6, padding: '8px 11px', fontSize: 12, marginBottom: 8 }}>
              조회 상한에 닿았다 — 받은 것이 전부가 아니다. 단위를 좁혀 다시 본다.
            </div>
          )}
          {meta.caution && (
            <div style={{ background: '#3a1b1b', color: '#e08a8a', borderRadius: 6, padding: '8px 11px', fontSize: 12, marginBottom: 8 }}>
              {meta.caution}
            </div>
          )}

          {err && (
            <div style={{ background: '#3a1b1b', color: '#e08a8a', borderRadius: 6, padding: '10px 12px', fontSize: 12 }}>
              {err}
            </div>
          )}

          {!err && !loading && trend.length === 0 && (
            <div style={{ background: '#2a233a', color: '#b79ae0', borderRadius: 6, padding: '10px 12px', fontSize: 12 }}>
              <b>아직 적재된 관측이 없다.</b> 0 이 아니라 아직 수집하지 않은 것이다 —
              <span style={{ fontFamily: MONO }}> /api/cron/demand-sync</span> 가 하루 1회 채운다.
            </div>
          )}

          {unitsForShown.length > 1 && (
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 8, alignItems: 'center' }}>
              <span style={{ fontSize: 10.5, fontFamily: MONO, color: 'var(--ink-400)' }}>단위</span>
              {unitsForShown.map(function (u) {
                var on = u === shownUnit;
                return (
                  <button key={u} onClick={function () { setUnit(u); load(shown, u); }}
                    title="단위마다 창이 달라 서로 다른 계열이다 — 이어붙이지 않는다"
                    style={{ background: on ? '#11303a' : 'transparent', color: on ? '#5fc7e0' : 'var(--ink-400)',
                             border: '1px solid var(--border)', borderRadius: 999, padding: '3px 11px', cursor: 'pointer', fontSize: 11.5 }}>
                    {u === 'date' ? '일' : u === 'week' ? '주' : '월'}
                  </button>
                );
              })}
            </div>
          )}

          {groups.length > 1 && (
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 6 }}>
              {groups.map(function (g) {
                var on = g === shown;
                return (
                  <button key={g} onClick={function () { setGroup(g); load(g, undefined); }}
                    style={{ background: on ? '#2a233a' : 'transparent', color: on ? '#b79ae0' : 'var(--ink-400)',
                             border: '1px solid var(--border)', borderRadius: 999, padding: '3px 11px', cursor: 'pointer', fontSize: 11.5 }}>
                    {g}
                  </button>
                );
              })}
            </div>
          )}
        </Card>

        {/* ③ 계열 — 마지막 구간은 잠정으로 구분해 그린다 */}
        {series.length > 0 && (
          <Card>
            <H4>
              {shown} · {shownUnit === 'date' ? '일별' : shownUnit === 'week' ? '주별' : '월별'} 상대 지수 ({series.length}점
              {folded ? ' 중 최근 ' + bars.length + '점' : ''})
            </H4>
            {folded > 0 && (
              <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 6 }}>
                앞쪽 <b>{folded}점</b>은 그래프에서 접었다 — 데이터에는 그대로 있다(회귀는 전 구간을 쓴다).
              </div>
            )}
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 4, height: 150, padding: '4px 0 0' }}>
              {bars.map(function (t) {
                var h = Math.max(2, (Number(t.ratio) / maxRatio) * 130);
                var prov = t.is_window_last;
                return (
                  <div key={t.rk} title={t.period + ' · ' + t.ratio + (prov ? ' (잠정)' : '')}
                       style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
                    <span style={{ fontSize: 9.5, fontFamily: MONO, color: prov ? '#f0b46a' : 'var(--ink-400)' }}>
                      {Number(t.ratio).toFixed(0)}
                    </span>
                    <div style={{
                      width: '100%', height: h, borderRadius: '3px 3px 0 0',
                      // 잠정값은 채우지 않고 빗금 — "확정값과 같은 막대"로 그리지 않는다
                      background: prov
                        ? 'repeating-linear-gradient(135deg,#f0b46a 0 3px,#3a2a12 3px 6px)'
                        : 'linear-gradient(180deg,#5fc7e0,#11303a)',
                    }} />
                    <span style={{ fontSize: 9, fontFamily: MONO, color: 'var(--ink-400)' }}>
                      {shownUnit === 'date' ? String(t.period).slice(5) : String(t.period).slice(2, 7)}
                    </span>
                  </div>
                );
              })}
            </div>
            <div style={{ marginTop: 10, fontSize: 11, color: 'var(--ink-400)', display: 'flex', gap: 14, flexWrap: 'wrap' }}>
              <span><i style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 2, background: '#5fc7e0', marginRight: 5 }} />확정 구간</span>
              <span><i style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 2, background: 'repeating-linear-gradient(135deg,#f0b46a 0 3px,#3a2a12 3px 6px)', marginRight: 5 }} />
                잠정 — {meta.provisional_note || '마지막 구간은 값이 움직인다'}</span>
            </div>
          </Card>
        )}

        {/* 쇼핑인사이트 — 실패도 그린다 */}
        {insight.length > 0 && (
          <Card>
            <H4>쇼핑인사이트 · 성별 · 연령 · 기기</H4>
            <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 8 }}>
              상대 <b>클릭비율</b>이다 — 매출·전환·구매자 수가 아니다. 응답에 구간 라벨이 없어 캡처일로만 줄을 세운다.
            </div>
            <div style={{ display: 'grid', gap: 8 }}>
              {insight.slice(0, 8).map(function (r) {
                if (!r.ok) {
                  return (
                    <div key={r.rk} style={{ fontSize: 11.5, color: '#e08a8a', fontFamily: MONO }}>
                      {r.stat_dt} · cid {r.cid} — 미수집: {r.note || '사유 미기록'}
                    </div>
                  );
                }
                return (
                  <div key={r.rk} style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'baseline', fontSize: 12 }}>
                    <span style={{ fontFamily: MONO, color: 'var(--ink-400)' }}>{r.stat_dt}</span>
                    <b>{r.seed_query || r.cid}</b>
                    <span>여 {r.female}% · 남 {r.male}%</span>
                    <span style={{ color: 'var(--ink-400)' }}>
                      10s {r.age_10s} · 20s {r.age_20s} · 30s {r.age_30s} · 40s {r.age_40s} · 50s {r.age_50s} · 60+ {r.age_60s_plus}
                    </span>
                    <span>모바일 {r.device_mobile}% / PC {r.device_pc}%</span>
                    <Badge tone="cyan">tier {r.tier || 'B'}</Badge>
                  </div>
                );
              })}
            </div>
          </Card>
        )}

        {/* ④ 관측 공백 — 숨기지 않는다 */}
        <Card style={{ borderColor: uncollected.length ? '#3a2a12' : 'var(--border)' }}>
          <H4>관측 공백</H4>
          {uncollected.length === 0 ? (
            <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>
              기록된 미수집 없음. <b>단, 아직 수집이 한 번도 안 돌았으면 이 칸도 비어 있다</b> — 위 n 을 함께 본다.
            </div>
          ) : (
            <div style={{ display: 'grid', gap: 5 }}>
              {uncollected.slice(0, 12).map(function (u, i) {
                return (
                  <div key={i} style={{ fontSize: 11.5, fontFamily: MONO, color: '#f0b46a' }}>
                    {u.stat_dt} · {u.fn} · {u.target} — {u.reason}
                  </div>
                );
              })}
              {uncollected.length > 12 && (
                <div style={{ fontSize: 11, color: 'var(--ink-400)' }}>+{uncollected.length - 12}건 더 (접음)</div>
              )}
            </div>
          )}
        </Card>
      </div>
    );
  }

  window.DemandTrendPanel = DemandTrendPanel;
})();
