/* Matrix Dashboard — the core info-viz surface.
   Renders multiple views:
     1) KPI strip (reach / CPM / CTR / 유효성 / 경쟁강도 / 예상예산)
     2) Age × Target heatmap (cells = reach)
     3) Time × Placement heatmap (cells = effective impressions)
     4) Correlation matrix (overlap / 상호 베타)
     5) Validity radar across archetypes
*/

const { useMemo: useMemoMD } = React;

function MatrixDashboard({ query, target, exposure, vizMode, motion3d }) {
  const result = useMemoMD(() => window.SimEngine.evalQuery(query), [query]);
  const matrix = useMemoMD(() => window.SimEngine.buildMatrix(query), [query]);
  const correlationLeaves = useMemoMD(
    () => query.flatMap(g => g.leaves).slice(0, 10),
    [query]
  );
  const correlation = useMemoMD(
    () => window.SimEngine.buildCorrelation(correlationLeaves),
    [correlationLeaves]
  );
  const archetypeRanks = useMemoMD(() => window.SimEngine.rankArchetypes(query), [query]);

  if (!result) {
    return <EmptyState />;
  }

  return (
    <div className="dash">
      <KPIRow result={result} exposure={exposure} />
      <div className="dash-grid">
        <div className="dash-col-main">
          <Card title="연령대별 도달 파악" tag={`타겟 ${matrix.rows.length}개 × 연령 ${matrix.cols.length}개 · ${vizMode === 'iso3d' ? '3D' : '2D'}`}>
            {vizMode === 'iso3d' && window.Matrix3D
              ? <window.Matrix3D matrix={matrix} />
              : <ReachMatrix matrix={matrix} mode={vizMode} />}
          </Card>
          <Card title="시간대 × 노출지면 효과" tag="시간·지면">
            <TimePlacementMatrix result={result} exposure={exposure} mode={vizMode} />
          </Card>
        </div>
        <div className="dash-col-side">
          <Card title="페르소나 매칭" tag="6개 아키타입">
            <ArchetypeRanks ranks={archetypeRanks} />
          </Card>
          <Card title="연령대 분포" tag="%">
            <AgeDistribution result={result} />
          </Card>
          <Card title="성별 비율" tag="여·남">
            <GenderSkew result={result} />
          </Card>
        </div>
      </div>
      <Card title="카테고리 간 중복·보완 관계" tag={`${correlation.paths.length} × ${correlation.paths.length}`}>
        <CorrelationMatrix data={correlation} mode={vizMode} />
      </Card>

      <style>{`
        .dash { display: flex; flex-direction: column; gap: 14px; padding: 14px 18px 28px; }
        .dash-grid {
          display: grid;
          grid-template-columns: 1.6fr 1fr;
          gap: 14px;
        }
        .dash-col-main, .dash-col-side {
          display: flex; flex-direction: column; gap: 14px;
        }
      `}</style>
    </div>
  );
}

function Card({ title, tag, children }) {
  return (
    <section className="panel">
      <div className="panel-head">
        <h3>{title}{tag && <span className="tag">{tag}</span>}</h3>
      </div>
      <div className="panel-body no-pad" style={{ padding: 14 }}>
        {children}
      </div>
    </section>
  );
}

function EmptyState() {
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      flex: 1, padding: 40, gap: 12, color: 'var(--text-mute)',
    }}>
      <div style={{ width: 200, height: 200, position: 'relative' }}>
        <div className="hatch" style={{ position: 'absolute', inset: 0, border: '1px dashed var(--border-strong)' }} />
        <div style={{
          position: 'absolute', inset: 30,
          border: '1px solid var(--signal)',
          opacity: 0.5,
        }} />
        <div style={{
          position: 'absolute', inset: 60,
          border: '1px solid var(--cyan)',
          opacity: 0.5,
          transform: 'rotate(45deg)',
        }} />
      </div>
      <div className="label">no query expression yet</div>
      <div style={{ fontSize: 13, color: 'var(--text-dim)', textAlign: 'center', maxWidth: 360 }}>
        좌측 트리에서 카테고리를 선택해 그룹을 구성하면<br/>
        시뮬레이션 매트릭스가 생성됩니다.
      </div>
    </div>
  );
}

// ===== KPI strip =====

function KPIRow({ result, exposure }) {
  const fmt = window.SimEngine.fmtN;
  const fmtKRW = window.SimEngine.fmtKRW;

  // budget feasibility: result.budget vs exposure.budget * 30 days
  const monthly = exposure.budget * 30;
  const budgetRatio = result.budget / Math.max(1, monthly);
  const budgetStatus = budgetRatio < 0.7 ? 'plant' : budgetRatio < 1.2 ? 'signal' : 'alert';

  const kpis = [
    { name: '예상 도달 인원',  val: fmt(result.finalReach), sub: result.finalReach.toLocaleString() + ' 명',         tone: 'cyan' },
    { name: '월 노출수',     val: fmt(result.impressions), sub: '예상 노출 횟수',                                  tone: '' },
    { name: '클릭률',            val: result.ctr + '%',       sub: '평균 ' + (result.ctr*1).toFixed(2) + '%',                  tone: 'signal' },
    { name: '1,000회 노출당 비용', val: '₩' + fmt(result.cpm),  sub: '입찰 ' + exposure.bid,                              tone: '' },
    { name: '전환률',            val: result.conv + '%',      sub: fmt(result.convs) + '건 전환',                         tone: 'plant' },
    { name: '타겟 정확도',  val: result.validity + '점',  sub: validityLabel(result.validity),                       tone: validityTone(result.validity) },
    { name: '경쟁도',            val: result.compete + '점', sub: competeLabel(result.compete),                         tone: result.compete > 70 ? 'alert' : '' },
    { name: '월 예상 지출',   val: fmtKRW(result.budget),  sub: `한도 ${(budgetRatio*100).toFixed(0)}% 사용`,           tone: budgetStatus },
  ];

  return (
    <div className="kpi-row">
      {kpis.map(k => (
        <KPIBlock key={k.name} {...k} />
      ))}
      <style>{`
        .kpi-row {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
          gap: 12px;
          position: relative;
          z-index: 1;
        }
        .kpi { border-radius: 0; }
      `}</style>
    </div>
  );
}

function KPIBlock({ name, val, sub, tone, numeric }) {
  return (
    <div className={'kpi ' + tone}>
      <div className="name">{name}</div>
      <div className="val">{val}</div>
      <div className="sub">{sub}</div>
    </div>
  );
}

function validityLabel(v) {
  if (v >= 75) return '매우 우수';
  if (v >= 55) return '양호';
  if (v >= 40) return '보통';
  return '개선 필요';
}
function validityTone(v) {
  if (v >= 75) return 'plant';
  if (v >= 55) return 'signal';
  if (v >= 40) return 'cyan';
  return 'alert';
}
function competeLabel(c) {
  if (c >= 80) return '레드오션';
  if (c >= 60) return '경합';
  if (c >= 40) return '중간';
  return '여유';
}

// ===== Reach Matrix =====

function ReachMatrix({ matrix, mode }) {
  if (!matrix.rows.length) return <Empty />;
  const max = Math.max(1, ...matrix.rows.flatMap(r => r.cells));

  // mode: 'heatmap' | 'dot' | 'bars' | 'numeric' | 'glyph'
  return (
    <div className="rmx">
      <div
        className="matrix-grid"
        style={{
          gridTemplateColumns: `180px repeat(${matrix.cols.length}, minmax(36px, 1fr))`,
          gridAutoRows: 'var(--cell-h)',
        }}
      >
        <div></div>
        {matrix.cols.map(c => (
          <div key={c} className="matrix-collabel">{c}</div>
        ))}
        {matrix.rows.map((row, ri) => (
          <React.Fragment key={ri}>
            <div className="matrix-rowlabel" title={row.path}>{row.label}</div>
            {row.cells.map((v, ci) => (
              <Cell key={ci} value={v} max={max} mode={mode} />
            ))}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

function Cell({ value, max, mode }) {
  const intensity = Math.min(1, value / max);
  const bg = `oklch(0.20 0.04 70 / 1) ${ Math.round((1-intensity)*100) }%`;
  // We want a heatmap; use solid background gradient toward amber.
  const fill = `oklch(${0.18 + intensity * 0.55} ${0.04 + intensity * 0.14} 72)`;

  if (mode === 'dot') {
    const sz = Math.max(2, intensity * 22);
    return (
      <div className="matrix-cell" style={{ background: 'var(--ink-100)' }} title={value.toLocaleString()}>
        <span style={{
          width: sz, height: sz, borderRadius: '50%',
          background: `oklch(0.80 ${0.14 * intensity} 72)`,
          display: 'block',
        }}/>
      </div>
    );
  }
  if (mode === 'bars') {
    return (
      <div className="matrix-cell" style={{ background: 'var(--ink-100)' }} title={value.toLocaleString()}>
        <div style={{
          width: '78%',
          height: `${Math.max(6, intensity * 100)}%`,
          background: fill,
          alignSelf: 'flex-end',
          marginTop: 'auto',
        }}/>
      </div>
    );
  }
  if (mode === 'numeric') {
    return (
      <div className="matrix-cell" style={{ background: 'var(--ink-100)' }} title={value.toLocaleString()}>
        <span className="v" style={{ color: 'var(--bone-200)' }}>
          {value >= 10000 ? (value/10000).toFixed(1) + '만' : value >= 1000 ? (value/1000).toFixed(1) + 'K' : value}
        </span>
      </div>
    );
  }
  if (mode === 'glyph') {
    // ascii ramp
    const chars = ' ·∶∷▪◾█';
    const ch = chars[Math.floor(intensity * (chars.length-1))];
    return (
      <div className="matrix-cell" style={{ background: 'var(--ink-100)' }} title={value.toLocaleString()}>
        <span className="v" style={{ fontSize: 16, color: `oklch(0.80 0.14 72)`, opacity: 0.3 + intensity * 0.7 }}>{ch}</span>
      </div>
    );
  }
  // default heatmap
  return (
    <div className="matrix-cell" style={{ background: fill }} title={value.toLocaleString()}>
      {intensity > 0.4 && (
        <span className="v">
          {value >= 10000 ? (value/10000).toFixed(0) + 'K' : value}
        </span>
      )}
    </div>
  );
}

// ===== Time × Placement Matrix =====

function TimePlacementMatrix({ result, exposure, mode }) {
  const times = window.SimEngine.TIME_BINS;
  const placements = window.SimEngine.PLACEMENTS;

  // matrix[t][p] = reach * timeDist[t] * placeNorm[p]
  const cells = times.map((t, ti) =>
    placements.map((p, pi) => {
      const onTime = exposure.times.includes(t.id) ? 1 : 0.25;
      const onPlace = exposure.placements.includes(p.id) ? 1 : 0.25;
      return Math.round(result.impressions * result.timeDist[ti] * result.placeNorm[pi] * onTime * onPlace);
    })
  );
  const max = Math.max(1, ...cells.flat());

  return (
    <div className="matrix-grid"
      style={{
        gridTemplateColumns: `120px repeat(${times.length}, 1fr)`,
        gridAutoRows: 'var(--cell-h)',
      }}>
      <div></div>
      {times.map(t => (
        <div key={t.id} className="matrix-rowlabel" style={{
          writingMode: 'horizontal-tb', transform: 'none',
          justifyContent: 'center',
        }}>{t.label}</div>
      ))}
      {placements.map((p, pi) => (
        <React.Fragment key={p.id}>
          <div className="matrix-rowlabel">{p.label}</div>
          {times.map((t, ti) => (
            <Cell key={ti} value={cells[ti][pi]} max={max} mode={mode} />
          ))}
        </React.Fragment>
      ))}
    </div>
  );
}

// ===== Correlation matrix =====

function CorrelationMatrix({ data, mode }) {
  if (data.paths.length < 2) {
    return <Empty msg="2개 이상 카테고리 필요" />;
  }
  const N = data.paths.length;
  const labels = data.paths.map(p => window.SimEngine.shortLabel(p));

  return (
    <div style={{ overflowX: 'auto' }}>
      <div className="matrix-grid"
        style={{
          gridTemplateColumns: `160px repeat(${N}, 28px)`,
          gridAutoRows: '28px',
          width: 'max-content',
        }}>
        <div></div>
        {labels.map((l, i) => (
          <div key={i} className="matrix-collabel" title={data.paths[i]}>{l}</div>
        ))}
        {labels.map((l, ri) => (
          <React.Fragment key={ri}>
            <div className="matrix-rowlabel" title={data.paths[ri]}>{l}</div>
            {data.matrix[ri].map((v, ci) => (
              <CorrCell key={ci} value={v} self={ri === ci} />
            ))}
          </React.Fragment>
        ))}
      </div>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12, marginTop: 14,
        fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-mute)'
      }}>
        <span>RELATION:</span>
        <Legend label="중복(베타)" color="oklch(0.78 0.14 70)" />
        <Legend label="보완"       color="oklch(0.78 0.12 220)" />
        <Legend label="독립"       color="oklch(0.28 0.02 70)" />
      </div>
    </div>
  );
}
function Legend({ label, color }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
      <span style={{ width: 10, height: 10, background: color, display: 'inline-block' }} />
      {label}
    </span>
  );
}
function CorrCell({ value, self }) {
  if (self) {
    return <div className="matrix-cell" style={{ background: 'var(--ink-200)' }}>
      <span style={{ color: 'var(--text-mute)', fontSize: 9 }}>—</span>
    </div>;
  }
  // value in [0,1]; high = overlap (signal), low = complementary (cyan)
  let bg;
  if (value > 0.8)      bg = `oklch(0.65 0.18 70 / ${Math.min(1, (value-0.5)*1.6)})`;
  else if (value > 0.5) bg = `oklch(0.5 0.10 70 / ${value*0.7})`;
  else if (value > 0.3) bg = `oklch(0.4 0.06 220 / ${(0.5-value)*1.4})`;
  else                  bg = `oklch(0.6 0.12 220 / ${(0.4-value)*2})`;
  return (
    <div className="matrix-cell" style={{ background: bg }} title={value.toFixed(2)}>
      <span style={{ fontSize: 9, color: 'var(--bone-100)', opacity: 0.8 }}>
        {value.toFixed(2)}
      </span>
    </div>
  );
}

// ===== Archetype ranks =====

function ArchetypeRanks({ ranks }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      {ranks.map(a => (
        <div key={a.id} className="arch-row">
          <div>
            <div style={{ fontSize: 11, color: 'var(--text)' }}>{a.name}</div>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text-mute)' }}>{a.desc}</div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <div className="bar" style={{ width: 60 }}>
              <div style={{ width: a.match + '%', background: a.match > 60 ? 'var(--plant)' : a.match > 40 ? 'var(--signal)' : 'var(--alert-dim)' }} />
            </div>
            <span className="mono" style={{ fontSize: 10, color: 'var(--text)', minWidth: 24, textAlign: 'right' }}>
              {a.match}
            </span>
          </div>
        </div>
      ))}
      <style>{`
        .arch-row {
          display: flex; justify-content: space-between; align-items: center;
          gap: 8px;
          padding: 4px 0;
          border-bottom: 1px dashed var(--border);
        }
        .arch-row:last-child { border-bottom: none; }
      `}</style>
    </div>
  );
}

// ===== Age distribution =====

function AgeDistribution({ result }) {
  const ages = window.SimEngine.AGES;
  const max = Math.max(...result.ageDist);

  return (
    <div>
      {ages.map((a, i) => (
        <div key={a} className="bar-row">
          <span className="l">{a}</span>
          <div className="bar"><div style={{ width: (result.ageDist[i]/max*100) + '%' }} /></div>
          <span className="v">{(result.ageDist[i]*100).toFixed(1)}%</span>
        </div>
      ))}
    </div>
  );
}

// ===== Gender skew =====

function GenderSkew({ result }) {
  const skew = result.skew; // 0..1 (1 = female)
  const fPct = Math.round(skew * 100);
  const mPct = 100 - fPct;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      <div style={{ display: 'flex', height: 22, fontFamily: 'var(--font-mono)', fontSize: 10 }}>
        <div style={{
          width: fPct + '%', background: 'oklch(0.78 0.15 350)',
          color: 'var(--ink-000)', display: 'grid', placeItems: 'center',
        }}>F {fPct}%</div>
        <div style={{
          width: mPct + '%', background: 'oklch(0.78 0.12 220)',
          color: 'var(--ink-000)', display: 'grid', placeItems: 'center',
        }}>M {mPct}%</div>
      </div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-mute)' }}>
        Skew Index: <span style={{ color: 'var(--text)' }}>{(skew*100).toFixed(0)}</span> / 50=중립
      </div>
    </div>
  );
}

function Empty({ msg }) {
  return <div style={{ padding: 20, color: 'var(--text-mute)', fontSize: 11, textAlign: 'center' }}>
    {msg || '데이터 없음'}
  </div>;
}

Object.assign(window, { MatrixDashboard: React.memo(MatrixDashboard) });
