// proto-common.jsx — shared primitives, data, router

// ─── Categories ─────────────────────────────────────────────────
const MUSIC_URL = 'https://random-player.vercel.app/';

const CATS = {
  briefing: { key: 'briefing', name: 'MATE Briefing',  short: 'BRIEFING', kor: '오늘의 브리핑',  blurb: '매일 아침 06:00, 자동 갱신',  href: 'briefing/index.html', external: false },
  study:    { key: 'study',    name: 'Study',          short: 'STUDY',    kor: '전체 개념도',     blurb: '구축 계획 · 아키텍처 · 운영', href: '#/study',              external: false },
  music:    { key: 'music',    name: 'Music Player',   short: 'MUSIC',    kor: '랜덤 플레이어',    blurb: '새 탭으로 열기 ↗',           href: MUSIC_URL,              external: true  },
  board:    { key: 'board',    name: '게시판',          short: 'BOARD',    kor: '토론 · 질문',     blurb: '비로그인 · 닉네임만 있으면', href: '#/board',              external: false },
  project:  { key: 'project',  name: 'My Project',     short: 'PROJECT',  kor: 'About MATE',     blurb: '무엇을 · 왜 · 어떻게',       href: '#/project',            external: false },
};

// ─── Hash router ────────────────────────────────────────────────
function useHash() {
  const [h, setH] = React.useState(() => window.location.hash || '#/');
  React.useEffect(() => {
    const onChange = () => setH(window.location.hash || '#/');
    window.addEventListener('hashchange', onChange);
    return () => window.removeEventListener('hashchange', onChange);
  }, []);
  return h;
}

function parseRoute(hash) {
  // Strip leading '#/' and split
  const path = (hash || '#/').replace(/^#\/?/, '');
  const parts = path.split('/').filter(Boolean);
  return parts;
}

function go(path) {
  window.location.hash = path.startsWith('#') ? path : `#${path.startsWith('/') ? path : '/' + path}`;
  window.scrollTo({ top: 0, behavior: 'smooth' });
}

// ─── Date helpers ───────────────────────────────────────────────
const KOREAN_DAY = ['일','월','화','수','목','금','토'];
function todayLabel(d = new Date()) {
  return `${d.getFullYear()}. ${String(d.getMonth() + 1).padStart(2,'0')}. ${String(d.getDate()).padStart(2,'0')} ${KOREAN_DAY[d.getDay()]}`;
}
function relTime(ts) {
  const d = (Date.now() - ts) / 1000;
  if (d < 60)      return '방금';
  if (d < 3600)    return `${Math.floor(d / 60)}분 전`;
  if (d < 86400)   return `${Math.floor(d / 3600)}시간 전`;
  if (d < 86400*7) return `${Math.floor(d / 86400)}일 전`;
  const dt = new Date(ts);
  return `${String(dt.getMonth()+1).padStart(2,'0')}.${String(dt.getDate()).padStart(2,'0')}`;
}

// ─── Top nav ────────────────────────────────────────────────────
function TopNav({ route }) {
  const at = (key) => route[0] === key ? 'active' : '';
  return (
    <header className="topnav">
      <div className="brand">
        <a href="#/" className="logo">MATE</a>
        <span className="tag">Melony AI Trading Engine</span>
      </div>
      <nav className="links">
        <a href="briefing/index.html">Briefing</a>
        <a href="#/study"    className={at('study')}>Study</a>
        <a href={MUSIC_URL} target="_blank" rel="noopener noreferrer">Music ↗</a>
        <a href="#/board"    className={at('board')}>게시판</a>
        <a href="#/project"  className={at('project')}>Project</a>
      </nav>
      <div className="meta">
        <span><span className="dot" />live</span>
        <span style={{fontFamily:'var(--font-mono)', fontSize: 12}}>mate-portal.pages.dev</span>
      </div>
    </header>
  );
}

// ─── Page footer ────────────────────────────────────────────────
function PageFoot() {
  return (
    <footer className="foot">
      <div>
        © 2026 MATE · Melony AI Trading Engine · Built on Cloudflare Pages
      </div>
      <div className="links">
        <a href="#/study">아키텍처</a>
        <a href="briefing/index.html">Briefing</a>
        <a href="https://github.com" target="_blank" rel="noopener noreferrer">GitHub ↗</a>
      </div>
    </footer>
  );
}

// ─── CatTag pill ────────────────────────────────────────────────
function CatTag({ k }) {
  const c = CATS[k];
  return (
    <span className={`cat-tag ${k}`}>
      <span className="swatch" />
      {c.short}
    </span>
  );
}

// ─── Toast (window.toast(msg)) ─────────────────────────────────
function ToastHost() {
  const [msg, setMsg] = React.useState(null);
  React.useEffect(() => {
    window.toast = (m) => {
      setMsg(m);
      setTimeout(() => setMsg(null), 2400);
    };
    return () => { delete window.toast; };
  }, []);
  if (!msg) return null;
  return <div className="toast">{msg}</div>;
}

// ─── Mock today's briefing ──────────────────────────────────────
const BRIEFING_TODAY = {
  date: todayLabel(),
  weather: { hi: 22, lo: 14, label: '맑음, 미세먼지 보통' },
  schedule: [
    { t: '10:00', s: 'Melony CTO 1on1' },
    { t: '14:30', s: 'Mate 인프라 점검' },
    { t: '20:00', s: '회고 · 내일 계획' },
  ],
  insight: [
    '“스팸이라도 오는 게 절간보다 낫다.”',
    '— 비로그인 게시판 운영 방침 (CTO)',
  ],
  highlights: [
    { tag: 'NEWS',  text: 'Cloudflare D1, 무료 한도 2배 확장' },
    { tag: 'STUDY', text: '오늘 학습: Pages Functions 인증 패턴' },
    { tag: 'BOARD', text: '“브리핑 항목 추가” 토론에 댓글 4개' },
  ],
};

// ─── Seed posts (used when localStorage is empty) ───────────────
const SEED_POSTS = [
  { id: 'p1', tag: '토론', title: '브리핑에 들어갈 항목, 더 추가하면 어떨까요?', nick: 'melony', email: '', ts: Date.now() - 1000*60*15, views: 38, body: '아침 브리핑이 너무 단순한 것 같아요. 날씨/일정/한 줄 외에\n\n- 어제 게시판 활동 요약\n- 오늘 GitHub 푸시 예정 작업\n- 가장 많이 읽은 글 TOP 3\n\n같은 걸 추가하면 좋겠습니다. 어떻게 생각하세요?',
    comments: [
      { nick: '맥클', ts: Date.now() - 1000*60*10, body: '게시판 활동 요약은 D1 쿼리 하나면 됩니다. 06시 크론에 묶을게요.' },
      { nick: 'guest_29', ts: Date.now() - 1000*60*7, body: 'GitHub 푸시 예정은 README의 Roadmap 섹션을 파싱해서 보여주는 건 어때요?' },
    ] },
  { id: 'p2', tag: '질문', title: 'Cloudflare D1 무료 한도가 정확히 얼마인가요', nick: 'guest_29', email: '', ts: Date.now() - 1000*60*60, views: 71, body: '게시판을 D1으로 올리려고 합니다. 무료 한도가 얼마까지인지 정확히 알고 계신 분 있나요?', comments: [{ nick: '맥클', ts: Date.now() - 1000*60*55, body: 'Workers 무료 플랜 기준 5GB 스토리지, 5M reads/day, 100K writes/day입니다. 개인 게시판이면 평생 안 닿아요.' }] },
  { id: 'p3', tag: '토론', title: 'Music Player를 외부 링크 말고 임베드로 넣으면 안 될까?', nick: '맥클', email: '', ts: Date.now() - 1000*60*60*3, views: 102, body: 'iframe으로 임베드하면 페이지 이탈 없이 들을 수 있는데, CSP/CORS 문제 때문에 외부 링크로 결정한 거 맞나요?', comments: [] },
  { id: 'p4', tag: '질문', title: 'Study 페이지의 전체 개념도, 어디서 보나요?', nick: 'anon', email: '', ts: Date.now() - 1000*60*60*5, views: 24, body: '메뉴에서 안 보여요.', comments: [] },
  { id: 'p5', tag: '잡담', title: '도메인은 mate.* 중에 어디가 좋을까',           nick: 'melony', email: '', ts: Date.now() - 1000*60*60*26, views: 188, body: 'mate.kr / mate.dev / withmate.com 중에 고민 중. 의견 부탁드려요.', comments: [{nick:'guest_03', ts: Date.now()-1000*60*60*20, body: '.dev 좋아요. 개발자스러운 느낌.'}, {nick:'anon', ts: Date.now()-1000*60*60*15, body: 'withmate가 친근해요.'}] },
  { id: 'p6', tag: '토론', title: '비로그인이라 닉네임 중복 어떻게 처리?',         nick: 'guest_03', email: '', ts: Date.now() - 1000*60*60*48, views: 67, body: '같은 닉네임을 여러 명이 쓰면 누가 누구인지 구분이 안 되는데, 이메일+닉네임을 묶는다거나 색깔 아바타를 자동 부여한다거나 해야 하지 않을까요?', comments: [] },
  { id: 'p7', tag: '질문', title: 'Turnstile 키 발급이 안 되는데 도와주세요',     nick: 'anon', email: '', ts: Date.now() - 1000*60*60*72, views: 45, body: 'Cloudflare 대시보드 어디에서 키를 받나요?', comments: [] },
];

// ─── Posts store (localStorage) ─────────────────────────────────
const STORAGE_KEY = 'mate.board.posts.v1';

function loadPosts() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return SEED_POSTS.slice();
    const arr = JSON.parse(raw);
    if (!Array.isArray(arr) || arr.length === 0) return SEED_POSTS.slice();
    return arr;
  } catch { return SEED_POSTS.slice(); }
}
function savePosts(arr) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(arr)); } catch {}
}
function resetPosts() { try { localStorage.removeItem(STORAGE_KEY); } catch {} }

// Subscriber list so views update when posts mutate
const _subs = new Set();
function usePosts() {
  const [posts, setPosts] = React.useState(loadPosts);
  React.useEffect(() => {
    const fn = () => setPosts(loadPosts());
    _subs.add(fn);
    return () => { _subs.delete(fn); };
  }, []);
  return posts;
}
function notify() { _subs.forEach(fn => fn()); }

function addPost(post) {
  const arr = loadPosts();
  const id = 'p' + (Date.now().toString(36));
  const next = [{ ...post, id, ts: Date.now(), views: 0, comments: [] }, ...arr];
  savePosts(next);
  notify();
  return id;
}
function addComment(postId, comment) {
  const arr = loadPosts();
  const next = arr.map(p => p.id === postId ? { ...p, comments: [...(p.comments || []), { ...comment, ts: Date.now() }] } : p);
  savePosts(next);
  notify();
}
function bumpView(postId) {
  const arr = loadPosts();
  const next = arr.map(p => p.id === postId ? { ...p, views: (p.views || 0) + 1 } : p);
  savePosts(next);
  notify();
}

Object.assign(window, {
  CATS, MUSIC_URL, useHash, parseRoute, go,
  KOREAN_DAY, todayLabel, relTime,
  TopNav, PageFoot, CatTag, ToastHost,
  BRIEFING_TODAY, SEED_POSTS,
  loadPosts, savePosts, resetPosts, usePosts,
  addPost, addComment, bumpView,
});
