/**
 * js/react-app.jsx
 * OP.GG / Lolchess Style Modern Game Information Hub - React Architecture
 */

const { useState, useEffect, useCallback, useRef, useMemo } = React;

// --------------------------------------------------------------------------
// MOCK DATA & CONSTANTS FOR META DECK & COUPONS
// --------------------------------------------------------------------------
const INITIAL_META_DECKS = [
  {
    id: 1,
    tier: 'S',
    title: '쌍검/태도 무협 극딜 빌드',
    winRate: 64.8,
    views: 12450,
    likes: 890,
    updatedAt: '10분 전',
    champions: [
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128',
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128',
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128'
    ]
  },
  {
    id: 2,
    tier: 'S',
    title: '연운 보스 파밍 방어 반사 덱',
    winRate: 61.2,
    views: 9820,
    likes: 712,
    updatedAt: '35분 전',
    champions: [
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128',
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128'
    ]
  },
  {
    id: 3,
    tier: 'A',
    title: '초반 원거리 암기 치명타 조합',
    winRate: 57.4,
    views: 6540,
    likes: 430,
    updatedAt: '1시간 전',
    champions: [
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128',
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128'
    ]
  },
  {
    id: 4,
    tier: 'B',
    title: '무과금 탐험 솔플 생존 세팅',
    winRate: 52.1,
    views: 4120,
    likes: 215,
    updatedAt: '3시간 전',
    champions: [
      'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128'
    ]
  }
];

const INITIAL_COUPONS = [
  { id: 'c1', reward: '7월 공식 특별 보상 쿠폰 (금화 5,000개 + 영약 상자)', code: 'WWM2026SPECIAL', isCopied: false },
  { id: 'c2', reward: '신규 문파 오픈 기념 쿠폰 (지원 상자 3개)', code: 'WWMSECRETGIFT', isCopied: false },
  { id: 'c3', reward: '주말 핫타임 재화 상자 지급 코드', code: 'WWMWEEKENDPVP', isCopied: false },
  { id: 'c4', reward: '출석 기념 한정판 스티커 팩 쿠폰', code: 'QINCHUANG0430', isCopied: false }
];

const BANNERS = [
  {
    id: 1,
    badge: 'RENEWAL',
    title: '연운 커뮤니티 허브 사이트 개선',
    bg: 'linear-gradient(135deg, #1b263b 0%, #0d1b2a 100%)'
  },
  {
    id: 2,
    badge: 'MAP & JOBO',
    title: '인터랙티브 지도 & 실시간 족보 검색',
    desc: '모든 위치 마커와 족보 힌트/정답을 빠르게 탐색해보세요.',
    bg: 'linear-gradient(135deg, #2b1e3a 0%, #150d2a 100%)'
  },
];

const CATEGORIES = [
  { key: 'all', label: '전체' },
  { key: 'notice', label: '공지' },
  { key: 'update', label: '알리미' },
  { key: 'coupon', label: '쿠폰' },
  { key: 'tip', label: '팁/공략' },
  { key: 'guild', label: '길드홍보' },
  { key: 'free', label: '자유' },
  { key: 'jobo', label: '족보' }
];

// --------------------------------------------------------------------------
// SKELETON LOADER COMPONENT
// --------------------------------------------------------------------------
function SkeletonLoader({ count = 5, mode = 'compact' }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
      {Array.from({ length: count }).map((_, idx) => (
        <div key={idx} className="hub-skeleton" style={{ height: mode === 'card' ? '120px' : '48px' }} />
      ))}
    </div>
  );
}

// --------------------------------------------------------------------------
// TOAST NOTIFICATION COMPONENT
// --------------------------------------------------------------------------
function Toast({ message, onClose }) {
  useEffect(() => {
    const timer = setTimeout(onClose, 3000);
    return () => clearTimeout(timer);
  }, [message, onClose]);

  if (!message) return null;

  return (
    <div className="hub-toast" role="alert">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
        <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
        <polyline points="22 4 12 14.01 9 11.01" />
      </svg>
      <span>{message}</span>
    </div>
  );
}

// --------------------------------------------------------------------------
// HEADER COMPONENT
// --------------------------------------------------------------------------
function Header({ onOpenLogin, user }) {
  return (
    <header className="hub-header">
      <a href="/" className="hub-header__brand">
        <img src="https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=256" alt="연운" className="hub-header__logo" />
        <span>연운 허브</span>
      </a>

      <nav className="hub-header__nav">
        <a href="/" className="hub-header__nav-link is-active">홈 대시보드</a>
        <a href="/map" className="hub-header__nav-link">인터랙티브 지도</a>
        <a href="/shop.html" className="hub-header__nav-link">포인트 샵</a>
      </nav>

      <div className="hub-header__actions">
        {user ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
            <span style={{ fontSize: '0.85rem', fontWeight: 600, color: 'var(--c-text-primary)' }}>{user.nickname || '검객'}님</span>
            <button className="hub-btn-primary" onClick={() => window.dispatchEvent(new CustomEvent('logout'))}>로그아웃</button>
          </div>
        ) : (
          <button className="hub-btn-primary" onClick={onOpenLogin}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
              <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" />
              <polyline points="10 17 15 12 10 7" />
              <line x1="15" y1="12" x2="3" y2="12" />
            </svg>
            로그인
          </button>
        )}
      </div>
    </header>
  );
}

// --------------------------------------------------------------------------
// HERO SEARCH COMPONENT
// --------------------------------------------------------------------------
function HeroSearch({ onSearch, recentSearches, onRemoveRecent }) {
  const [inputVal, setInputVal] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (inputVal.trim()) {
      onSearch(inputVal.trim());
    }
  };

  return (
    <div className="hub-hero-search">
      <form onSubmit={handleSubmit} className="hub-search-box">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--c-text-muted)" strokeWidth="2.5" style={{ marginRight: '10px' }}>
          <circle cx="11" cy="11" r="8" />
          <path d="m21 21-4.3-4.3" />
        </svg>
        <input
          type="text"
          className="hub-search-box__input"
          placeholder="챔피언, 문파 공략, 족보 퀴즈, 쿠폰 코드 검색..."
          value={inputVal}
          onChange={(e) => setInputVal(e.target.value)}
        />
        <button type="submit" className="hub-search-box__submit">
          검색
        </button>
      </form>

      <div className="hub-search-tags">
        <span className="hub-search-tag-title">최근 검색:</span>
        {recentSearches.length === 0 ? (
          <span style={{ color: 'var(--c-text-muted)', fontSize: '0.8rem' }}>검색 기록이 없습니다.</span>
        ) : (
          recentSearches.map((item, idx) => (
            <span key={idx} className="hub-tag" onClick={() => { setInputVal(item); onSearch(item); }}>
              {item}
            </span>
          ))
        )}
      </div>

      <div className="hub-search-tags">
        <span className="hub-search-tag-title">인기 검색어:</span>
        <span className="hub-tag hub-tag--hot" onClick={() => { setInputVal('쌍검 빌드'); onSearch('쌍검 빌드'); }}>🔥 쌍검 빌드</span>
        <span className="hub-tag hub-tag--hot" onClick={() => { setInputVal('문파 족보'); onSearch('문파 족보'); }}>🔥 문파 족보</span>
        <span className="hub-tag" onClick={() => { setInputVal('쿠폰'); onSearch('쿠폰'); }}>7월 쿠폰</span>
        <span className="hub-tag" onClick={() => { setInputVal('보스 패턴'); onSearch('보스 패턴'); }}>보스 패턴</span>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------------
// HERO CAROUSEL COMPONENT
// --------------------------------------------------------------------------
function HeroCarousel() {
  const [activeIndex, setActiveIndex] = useState(0);
  const isHovered = useRef(false);

  useEffect(() => {
    const timer = setInterval(() => {
      if (!isHovered.current) {
        setActiveIndex((prev) => (prev + 1) % BANNERS.length);
      }
    }, 3000);
    return () => clearInterval(timer);
  }, []);

  return (
    <div
      className="hub-carousel"
      onMouseEnter={() => isHovered.current = true}
      onMouseLeave={() => isHovered.current = false}
    >
      {BANNERS.map((banner, index) => (
        <div
          key={banner.id}
          className={`hub-carousel__slide ${index === activeIndex ? 'is-active' : ''}`}
          style={{ background: banner.bg }}
        >
          <div className="hub-carousel__overlay" />
          <div className="hub-carousel__content">
            <span className="hub-carousel__badge">{banner.badge}</span>
            <h2 className="hub-carousel__title">{banner.title}</h2>
            <p className="hub-carousel__desc">{banner.desc}</p>
          </div>
        </div>
      ))}

      <div className="hub-carousel__indicators">
        {BANNERS.map((_, index) => (
          <button
            key={index}
            className={`hub-carousel__dot ${index === activeIndex ? 'is-active' : ''}`}
            onClick={() => setActiveIndex(index)}
            aria-label={`슬라이드 ${index + 1}`}
          />
        ))}
      </div>
    </div>
  );
}

// --------------------------------------------------------------------------
// HOT META DECK COMPONENT (LOLCHESS STYLE)
// --------------------------------------------------------------------------
function HotMetaDeck({ decks, onSelectDeck }) {
  return (
    <section className="hub-meta-deck-section">
      <div className="hub-section-header">
        <h3 className="hub-section-title">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
            <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
          </svg>
          Today's Hot Meta
        </h3>
      </div>

      <div className="hub-meta-grid">
        {decks.map((deck) => (
          <div key={deck.id} className="hub-meta-card" onClick={() => onSelectDeck(deck)}>
            <div className="hub-meta-card__top">
              <span className={`hub-tier-badge hub-tier-badge--${deck.tier}`}>
                TIER {deck.tier}
              </span>
              <span style={{ fontSize: '0.8rem', color: 'var(--c-text-muted)' }}>{deck.updatedAt}</span>
            </div>

            <h4 className="hub-meta-card__title">{deck.title}</h4>

            <div className="hub-meta-card__champions">
              {deck.champions.map((img, i) => (
                <img key={i} src={img} alt="챔피언" className="hub-champ-avatar" />
              ))}
            </div>

            <div className="hub-meta-card__stats">
              <span>승률: <strong style={{ color: 'var(--c-tier-b)' }}>{deck.winRate}%</strong></span>
              <span>조회: {deck.views.toLocaleString()}</span>
              <span>추천: {deck.likes}</span>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

// --------------------------------------------------------------------------
// COUPON QUICK COPY COMPONENT
// --------------------------------------------------------------------------
function CouponCardItem({ coupon, onCopy }) {
  return (
    <div className="hub-coupon-card">
      <div className="hub-coupon-info">
        <div className="hub-coupon-reward-scroll" title={coupon.reward || coupon.title}>
          <span className="hub-coupon-reward-text">
            {coupon.reward || coupon.title || '쿠폰 내용 없음'}
          </span>
        </div>
      </div>

      <div
        className={`hub-coupon-code-wrapper ${coupon.isCopied ? 'is-copied' : ''}`}
        onClick={() => onCopy(coupon)}
        title="클릭하여 코드 복사"
      >
        {coupon.isCopied ? (
          <>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3">
              <polyline points="20 6 9 17 4 12" />
            </svg>
            <span className="hub-coupon-code" style={{ color: 'var(--c-tier-b)' }}>복사 완료!</span>
          </>
        ) : (
          <>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
              <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>
            <span className="hub-coupon-code">{coupon.code}</span>
          </>
        )}
      </div>
    </div>
  );
}

function CouponQuickCopy({ coupons, onCopy }) {
  const [expanded, setExpanded] = useState(false);
  const initialCoupons = coupons.slice(0, 4);
  const extraCoupons = coupons.slice(4);

  return (
    <section className="hub-coupon-section">
      <div className="hub-section-header">
        <h3 className="hub-section-title">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
            <path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v2z" />
          </svg>
          퀵 쿠폰
        </h3>

        {coupons.length > 4 && (
          <button
            type="button"
            className="hub-more-btn"
            onClick={() => setExpanded(!expanded)}
          >
            <span>{expanded ? '접기' : `더보기 (${coupons.length})`}</span>
            <svg
              className={`hub-more-icon ${expanded ? 'is-open' : ''}`}
              width="14"
              height="14"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth="2.5"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <polyline points="6 9 12 15 18 9" />
            </svg>
          </button>
        )}
      </div>

      <div className="hub-coupon-grid">
        {initialCoupons.map((coupon) => (
          <CouponCardItem key={coupon.id} coupon={coupon} onCopy={onCopy} />
        ))}
      </div>

      {extraCoupons.length > 0 && (
        <div className={`hub-coupon-extra-container ${expanded ? 'is-expanded' : ''}`}>
          <div className="hub-coupon-extra-inner">
            <div className="hub-coupon-grid" style={{ marginTop: '14px' }}>
              {extraCoupons.map((coupon) => (
                <CouponCardItem key={coupon.id} coupon={coupon} onCopy={onCopy} />
              ))}
            </div>
          </div>
        </div>
      )}
    </section>
  );
}

// --------------------------------------------------------------------------
// CATEGORY TABS & VIEW SWITCHER COMPONENT
// --------------------------------------------------------------------------
function CategoryTabs({ currentTab, onTabChange, viewMode, onViewModeChange }) {
  return (
    <div className="hub-toolbar-row">
      <div className="hub-tabs" role="tablist">
        {CATEGORIES.map((cat) => (
          <button
            key={cat.key}
            role="tab"
            aria-selected={currentTab === cat.key}
            className={`hub-tab ${currentTab === cat.key ? 'is-active' : ''}`}
            onClick={() => onTabChange(cat.key)}
          >
            {cat.label}
          </button>
        ))}
      </div>

      <div className="hub-view-switcher">
        <button
          className={`hub-view-btn ${viewMode === 'compact' ? 'is-active' : ''}`}
          title="리스트 뷰"
          onClick={() => onViewModeChange('compact')}
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <line x1="8" y1="6" x2="21" y2="6" /><line x1="8" y1="12" x2="21" y2="12" /><line x1="8" y1="18" x2="21" y2="18" />
            <line x1="3" y1="6" x2="3.01" y2="6" /><line x1="3" y1="12" x2="3.01" y2="12" /><line x1="3" y1="18" x2="3.01" y2="18" />
          </svg>
        </button>
        <button
          className={`hub-view-btn ${viewMode === 'card' ? 'is-active' : ''}`}
          title="카드 뷰"
          onClick={() => onViewModeChange('card')}
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <rect x="3" y="3" width="7" height="7" /><rect x="14" y="3" width="7" height="7" />
            <rect x="14" y="14" width="7" height="7" /><rect x="3" y="14" width="7" height="7" />
          </svg>
        </button>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------------
// POST LIST & POST CARD COMPONENTS
// --------------------------------------------------------------------------
function PostCard({ post, viewMode, onClick }) {
  if (viewMode === 'card') {
    const imageSrc = post.image || post.thumbnail || (post.content && (() => {
      const match = post.content.match(/<img[^>]+src=["']([^"']+)["']/i) || post.content.match(/!\[.*?\]\((https?:\/\/[^\s\)]+)\)/);
      return match ? match[1] : null;
    })());

    const textSnippet = post.content 
      ? post.content.replace(/<[^>]*>?/gm, '').replace(/!\[.*?\]\(.*?\)/g, '').replace(/\[(.*?)\]\(.*?\)/g, '$1').trim()
      : '';

    return (
      <div className={`hub-post-card ${imageSrc ? 'has-media' : 'no-media'}`} onClick={onClick}>
        {imageSrc ? (
          <div className="hub-post-card__media">
            <img src={imageSrc} alt={post.title} className="hub-post-card__img" loading="lazy" />
            <div className="hub-post-card__gradient-overlay" />
          </div>
        ) : (
          <div className="hub-post-card__no-media-header">
            <span className="hub-category-badge">{post.category || '자유'}</span>
          </div>
        )}

        <div className="hub-post-card__body">
          {imageSrc && <span className="hub-category-badge" style={{ alignSelf: 'flex-start' }}>{post.category || '자유'}</span>}
          <h4 className="hub-post-card__title">{post.title}</h4>
          {!imageSrc && textSnippet && (
            <p className="hub-post-card__snippet">{textSnippet}</p>
          )}
        </div>

        <div className="hub-post-card__footer">
          <span>{post.author || '익명'}</span>
          <span>조회 {post.views || 0} · 추천 {post.likes || 0}</span>
        </div>
      </div>
    );
  }

  return (
    <div className="hub-post-row" onClick={onClick}>
      <div className="hub-post-row__main">
        <span className="hub-category-badge">{post.category || '자유'}</span>
        <span className="hub-post-row__title">{post.title}</span>
      </div>
      <div className="hub-post-row__meta">
        <span>{post.author || '익명'}</span>
        <span>{post.created_at ? new Date(post.created_at).toLocaleDateString() : '방금 전'}</span>
        <span>조회 {post.views || 0}</span>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------------
// JOBO (QUIZ TABLE) COMPONENT
// --------------------------------------------------------------------------
function JoboView() {
  const [joboData, setJoboData] = useState([]);
  const [query, setQuery] = useState('');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let isMounted = true;
    if (window.fetchJoboData) {
      window.fetchJoboData().then((data) => {
        if (isMounted) {
          setJoboData(data || []);
          setLoading(false);
        }
      }).catch(() => {
        if (isMounted) setLoading(false);
      });
    } else {
      setLoading(false);
    }
    return () => { isMounted = false; };
  }, []);

  const filteredData = useMemo(() => {
    if (!query.trim()) return joboData;
    const q = query.toLowerCase().trim();
    return joboData.filter((item) =>
      (item.hint && item.hint.toLowerCase().includes(q)) ||
      (item.answer && item.answer.toLowerCase().includes(q)) ||
      (item.user && item.user.toLowerCase().includes(q))
    );
  }, [joboData, query]);

  if (loading) {
    return <SkeletonLoader count={8} mode="compact" />;
  }

  return (
    <div id="jobo-root" style={{ display: 'flex', flexDirection: 'column', gap: '16px', marginTop: '12px' }}>
      <div className="jobo-search-container">
        <svg className="jobo-search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
          <circle cx="11" cy="11" r="8" />
          <path d="m21 21-4.3-4.3" />
        </svg>

        <input
          type="text"
          id="jobo-search-input"
          className="jobo-search-input"
          placeholder="힌트 또는 정답으로 족보 퀴즈 검색..."
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          autoComplete="off"
        />

        {query && (
          <button
            type="button"
            className="jobo-search-clear"
            title="지우기"
            onClick={() => setQuery('')}
          >
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3">
              <line x1="18" y1="6" x2="6" y2="18" />
              <line x1="6" y1="6" x2="18" y2="18" />
            </svg>
          </button>
        )}

        <span className="jobo-search-badge">
          {filteredData.length.toLocaleString()}개 족보
        </span>
      </div>

      <div className="jobo-quick-tags">
        <span className="jobo-quick-label">자주 찾는 퀴즈:</span>
        {['홍학', '펭귄', '메기', '너구리', '사막'].map((tag) => (
          <button key={tag} type="button" className="jobo-tag-chip" onClick={() => setQuery(tag)}>
            #{tag}
          </button>
        ))}
      </div>

      <div style={{ padding: '12px 16px', background: 'var(--c-bg-card)', borderRadius: 'var(--radius-md)', fontSize: '0.85rem', color: 'var(--c-text-secondary)', border: '1px dashed var(--c-border)', display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ color: 'var(--c-accent-blue)', flexShrink: 0, marginTop: '1px' }}>
          <circle cx="12" cy="12" r="10" />
          <path d="M12 16v-4" />
          <path d="M12 8h.01" />
        </svg>
        <div style={{ lineHeight: '1.4' }}>
          이 족보 콘텐츠의 퀴즈 문제 및 정답 데이터는 <a href="https://wwm.tips" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--c-accent-blue)', fontWeight: 500, textDecoration: 'underline' }}>wwm.tips</a> 사이트에서 제공받아 연동하고 있습니다.
        </div>
      </div>

      <div id="jobo-list-container">
        {filteredData.length === 0 ? (
          <div style={{ textAlign: 'center', padding: '48px 16px', color: 'var(--c-text-muted)' }}>
            검색 결과가 없습니다.
          </div>
        ) : (
          <div className="jobo-table-wrapper">
            <table className="jobo-table">
              <thead>
                <tr>
                  <th style={{ width: '50%' }}>힌트</th>
                  <th style={{ width: '30%' }}>정답</th>
                  <th style={{ width: '20%' }}>제공</th>
                </tr>
              </thead>
              <tbody>
                {filteredData.map((item, index) => (
                  <tr key={index}>
                    <td>{item.hint || '-'}</td>
                    <td className="jobo-table__answer">{item.answer || '-'}</td>
                    <td className="jobo-table__user">{item.user || '-'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}

function PostList({ posts, viewMode, loading, currentTab, onPostClick }) {
  if (currentTab === 'jobo') {
    return <JoboView />;
  }

  if (loading) {
    return <SkeletonLoader count={6} mode={viewMode} />;
  }

  if (!posts || posts.length === 0) {
    return (
      <div style={{ textAlign: 'center', padding: '48px 16px', color: 'var(--c-text-muted)' }}>
        등록된 게시글이 없습니다.
      </div>
    );
  }

  return (
    <div className={`hub-post-container ${viewMode === 'card' ? 'view-card' : ''}`}>
      {posts.map((post) => (
        <PostCard key={post.id} post={post} viewMode={viewMode} onClick={() => onPostClick(post)} />
      ))}
    </div>
  );
}

// --------------------------------------------------------------------------
// SIDEBAR COMPONENTS
// --------------------------------------------------------------------------
function UserCard({ user, onOpenLogin }) {
  return (
    <div className="hub-widget">
      <div className="hub-widget__header">
        <h4 className="hub-widget__title">내 정보</h4>
      </div>

      {user ? (
        <div className="hub-user-card">
          <img src={user.avatar || 'https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=128'} alt="아바타" className="hub-user-avatar" />
          <div className="hub-user-info">
            <span className="hub-user-name">{user.nickname || '검객'}</span>
            <span className="hub-user-points">⚡ {user.points || 1250} P</span>
          </div>
        </div>
      ) : (
        <div style={{ textAlign: 'center', padding: '8px 0' }}>
          <p style={{ fontSize: '0.85rem', color: 'var(--c-text-secondary)', marginBottom: '12px' }}>로그인하여 포인트를 획득하세요.</p>
          <button className="hub-btn-primary" style={{ width: '100%', justifyContent: 'center' }} onClick={onOpenLogin}>
            로그인 / 회원가입
          </button>
        </div>
      )}
    </div>
  );
}

function RankingWidget() {
  return (
    <div className="hub-widget">
      <div className="hub-widget__header">
        <h4 className="hub-widget__title">실시간 랭킹</h4>
      </div>
      <div className="hub-ranking-list" style={{ textAlign: 'center', padding: '20px 0', color: 'var(--c-text-muted)', fontSize: '0.85rem' }}>
        서비스 개선 중입니다.
      </div>
    </div>
  );
}

function MapShortcutWidget() {
  return (
    <a href="/map" className="hub-map-widget">
      <div>
        <div style={{ fontSize: '0.75rem', color: 'var(--c-accent-blue)', fontWeight: 700 }}>INTERACTIVE</div>
        <div style={{ fontSize: '1rem', fontWeight: 700, color: '#fff' }}>연운 인터랙티브 지도</div>
      </div>
      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
        <polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21" />
        <line x1="9" y1="3" x2="9" y2="18" />
        <line x1="15" y1="6" x2="15" y2="21" />
      </svg>
    </a>
  );
}

function Sidebar({ user, onOpenLogin }) {
  return (
    <aside className="hub-sidebar">
      <UserCard user={user} onOpenLogin={onOpenLogin} />
      <MapShortcutWidget />
      <RankingWidget />
    </aside>
  );
}

// --------------------------------------------------------------------------
// MOBILE BOTTOM NAV COMPONENT
// --------------------------------------------------------------------------
function MobileBottomNav({ currentTab, onTabChange, onOpenWriteModal }) {
  return (
    <nav className="hub-bottom-nav">
      <button
        className={`hub-bottom-nav__item ${currentTab === 'all' ? 'is-active' : ''}`}
        onClick={() => onTabChange('all')}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
          <path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
          <polyline points="9 22 9 12 15 12 15 22" />
        </svg>
        <span>홈</span>
      </button>

      <button
        className={`hub-bottom-nav__item ${currentTab === 'jobo' ? 'is-active' : ''}`}
        onClick={() => onTabChange('jobo')}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
          <circle cx="11" cy="11" r="8" />
          <path d="m21 21-4.3-4.3" />
        </svg>
        <span>족보</span>
      </button>

      <button
        className="hub-bottom-nav__item"
        style={{ color: 'var(--c-accent-blue)' }}
        onClick={onOpenWriteModal}
      >
        <div style={{ background: 'var(--c-accent-blue)', borderRadius: '50%', padding: '6px', color: '#fff', marginTop: '-12px', boxShadow: '0 4px 12px rgba(83, 131, 232, 0.4)' }}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.8">
            <line x1="12" y1="5" x2="12" y2="19" />
            <line x1="5" y1="12" x2="19" y2="12" />
          </svg>
        </div>
        <span>글쓰기</span>
      </button>

      <button
        className="hub-bottom-nav__item"
        onClick={() => window.location.href = '/map'}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
          <polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21" />
          <line x1="9" y1="3" x2="9" y2="18" />
          <line x1="15" y1="6" x2="15" y2="21" />
        </svg>
        <span>지도</span>
      </button>

      <button
        className="hub-bottom-nav__item"
        onClick={() => window.location.href = '/shop.html'}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
          <path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" />
          <path d="M3 6h18" />
        </svg>
        <span>포인트샵</span>
      </button>
    </nav>
  );
}

// --------------------------------------------------------------------------
// MOBILE EXCLUSIVE DASHBOARD COMPONENT
// --------------------------------------------------------------------------
function MobileDashboard({
  currentTab,
  setCurrentTab,
  viewMode,
  handleViewModeChange,
  posts,
  loading,
  coupons,
  handleCopyCoupon,
  handlePostClick,
  handleOpenWriteModal,
  user,
  onOpenLogin
}) {
  return (
    <div className="hub-mobile-layout">
      {/* Mobile Header */}
      <header className="hub-mobile-header">
        <a href="/" className="hub-mobile-brand">
          <img src="https://cdn.discordapp.com/emojis/1410879101194993744.webp?size=256" alt="연운" className="hub-mobile-brand-logo" />
          <span>연운 허브 M</span>
        </a>

        <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
          {user ? (
            <span style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--c-accent-blue)' }}>{user.nickname || '검객'}님</span>
          ) : (
            <button className="hub-btn-primary" style={{ padding: '6px 12px', fontSize: '0.8rem' }} onClick={onOpenLogin}>
              로그인
            </button>
          )}
        </div>
      </header>

      {/* Category Tabs (상단 헤더 탭) */}
      <CategoryTabs
        currentTab={currentTab}
        onTabChange={setCurrentTab}
        viewMode={viewMode}
        onViewModeChange={handleViewModeChange}
      />

      {/* Hero Banner Carousel */}
      <HeroCarousel />

      {/* Post List */}
      <div style={{ padding: '4px 0' }}>
        <PostList
          posts={posts}
          viewMode={viewMode}
          loading={loading}
          currentTab={currentTab}
          onPostClick={handlePostClick}
        />
      </div>

      {/* Mobile Bottom Fixed Nav */}
      <MobileBottomNav
        currentTab={currentTab}
        onTabChange={setCurrentTab}
        onOpenWriteModal={handleOpenWriteModal}
      />
    </div>
  );
}

// --------------------------------------------------------------------------
// MAIN APP COMPONENT
// --------------------------------------------------------------------------
function App() {
  const [isMobile, setIsMobile] = useState(() => window.innerWidth <= 768);
  const [currentTab, setCurrentTab] = useState('all');

  useEffect(() => {
    const handleResize = () => setIsMobile(window.innerWidth <= 768);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  const [viewMode, setViewMode] = useState(() => localStorage.getItem('wwm_view_mode') || 'compact');
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [toastMessage, setToastMessage] = useState('');
  const [recentSearches, setRecentSearches] = useState(['쌍검', '문파 족보', '보스']);
  const [coupons, setCoupons] = useState(INITIAL_COUPONS);
  const [metaDecks] = useState(INITIAL_META_DECKS);
  const [user, setUser] = useState(null);

  // View mode persistence
  const handleViewModeChange = (mode) => {
    setViewMode(mode);
    localStorage.setItem('wwm_view_mode', mode);
  };

  // Fetch Actual Coupons from 'coupon' category
  useEffect(() => {
    let isMounted = true;
    if (window.fetchPosts) {
      window.fetchPosts('coupon').then((res) => {
        if (isMounted && res.posts && res.posts.length > 0) {
          const loadedCoupons = res.posts.map((post, idx) => {
            let cleanContent = post.content ? post.content.replace(/<[^>]*>?/gm, '').trim() : '';
            const match = cleanContent ? cleanContent.match(/[A-Z0-9_]{5,25}/) : null;
            const codeVal = post.code || (post.title && /^[A-Z0-9_]{4,25}$/i.test(post.title.trim()) ? post.title.trim() : (match ? match[0] : 'WWM2026SPECIAL'));

            // 본문 시작 부분에 쿠폰 코드가 중복되면 잘라내기
            if (codeVal && cleanContent.startsWith(codeVal)) {
              cleanContent = cleanContent.slice(codeVal.length).trim();
            }

            // 줄바꿈을 한 줄 공백으로 펴주고 깔끔하게 보상명 추출
            let rewardVal = cleanContent.replace(/[\r\n]+/g, ' ').trim();
            if (!rewardVal) {
              rewardVal = (post.title && post.title !== codeVal) ? post.title : '게임 내 특별 보상 아이템';
            }

            return {
              id: `c_${post.id || idx}`,
              reward: rewardVal,
              code: codeVal,
              isCopied: false
            };
          });
          setCoupons(loadedCoupons);
        }
      }).catch((e) => console.warn('[Coupons] fetch failed', e));
    }
    return () => { isMounted = false; };
  }, []);

  // Fetch Posts based on Category Tab
  useEffect(() => {
    let isMounted = true;
    setLoading(true);

    if (window.fetchPosts) {
      window.fetchPosts(currentTab).then((res) => {
        if (isMounted) {
          setPosts(res.posts || []);
          setLoading(false);
        }
      }).catch(() => {
        if (isMounted) setLoading(false);
      });
    } else {
      setTimeout(() => {
        if (isMounted) setLoading(false);
      }, 500);
    }

    return () => { isMounted = false; };
  }, [currentTab]);

  // Handle Search
  const handleSearch = (keyword) => {
    if (!recentSearches.includes(keyword)) {
      setRecentSearches([keyword, ...recentSearches.slice(0, 4)]);
    }
    setToastMessage(`'${keyword}' 검색 결과를 불러옵니다.`);
  };

  // Handle Coupon Copy
  const handleCopyCoupon = (coupon) => {
    navigator.clipboard.writeText(coupon.code);
    setCoupons(coupons.map(c => c.id === coupon.id ? { ...c, isCopied: true } : c));
    setToastMessage(`쿠폰 코드 [${coupon.code}] 복사 완료!`);
  };

  // Trigger Write Modal
  const handleOpenWriteModal = () => {
    window.dispatchEvent(new CustomEvent('open-editor', { detail: { category: currentTab } }));
  };

  // Trigger Post Detail Modal
  const handlePostClick = (post) => {
    window.dispatchEvent(new CustomEvent('open-post-detail', { detail: { post } }));
  };

  if (isMobile) {
    return (
      <>
        <MobileDashboard
          currentTab={currentTab}
          setCurrentTab={setCurrentTab}
          viewMode={viewMode}
          handleViewModeChange={handleViewModeChange}
          posts={posts}
          loading={loading}
          coupons={coupons}
          handleCopyCoupon={handleCopyCoupon}
          handlePostClick={handlePostClick}
          handleOpenWriteModal={handleOpenWriteModal}
          user={user}
          onOpenLogin={() => window.dispatchEvent(new CustomEvent('open-login'))}
        />
        <Toast message={toastMessage} onClose={() => setToastMessage('')} />
      </>
    );
  }

  return (
    <div className="hub-layout">
      <Header onOpenLogin={() => window.dispatchEvent(new CustomEvent('open-login'))} user={user} />

      <HeroCarousel />

      <CouponQuickCopy coupons={coupons} onCopy={handleCopyCoupon} />

      <div className="hub-body">
        <main>
          <CategoryTabs
            currentTab={currentTab}
            onTabChange={setCurrentTab}
            viewMode={viewMode}
            onViewModeChange={handleViewModeChange}
          />

          <PostList
            posts={posts}
            viewMode={viewMode}
            loading={loading}
            currentTab={currentTab}
            onPostClick={handlePostClick}
          />
        </main>

        <Sidebar user={user} onOpenLogin={() => window.dispatchEvent(new CustomEvent('open-login'))} />
      </div>

      <button className="hub-fab" title="글 작성" onClick={handleOpenWriteModal}>
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
          <line x1="12" y1="5" x2="12" y2="19" />
          <line x1="5" y1="12" x2="19" y2="12" />
        </svg>
      </button>

      <Toast message={toastMessage} onClose={() => setToastMessage('')} />
    </div>
  );
}

// --------------------------------------------------------------------------
// MOUNT REACT APP
// --------------------------------------------------------------------------
const initReactHub = () => {
  const rootEl = document.getElementById('react-root');
  if (rootEl && !rootEl._reactRoot) {
    const root = ReactDOM.createRoot(rootEl);
    rootEl._reactRoot = root;
    root.render(<App />);
  }
};

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initReactHub);
} else {
  initReactHub();
}
