// Shared UI primitives used across frontend/user/admin.

// Generic placeholder box (striped SVG background)
const Placeholder = ({ label, h = 200, w = '100%', style = {} }) => (
  <div className="ph" style={{ height: h, width: w, borderRadius: 12, ...style }}>
    <span>{label}</span>
  </div>
);

// Brand lockup — 印 seal + serif wordmark
const Logo = ({ size = 22, showTag = false }) => (
  <div style={{ display:'flex', alignItems:'center', gap: 10 }}>
    <div className="seal" style={{ width: size*1.4, height: size*1.4, fontSize: size*0.95 }}>
      yy
    </div>
    <div style={{ display:'flex', flexDirection:'column', lineHeight:1 }}>
      <div style={{
        fontFamily:'var(--font-serif)', fontWeight:700, fontSize: size,
        letterSpacing:'0.02em', color:'var(--ink)',
      }}>YYLearn</div>
      {showTag && <div style={{ fontSize:10, color:'var(--ink-3)', marginTop:3, letterSpacing:'0.1em', textTransform:'uppercase' }}>{(() => { const l = (typeof localStorage !== 'undefined' && (localStorage.getItem('yylearn.lang') || localStorage.getItem('yy.lang'))) || 'en'; return l && l.startsWith('zh') ? '承东方之智' : 'Learn the East'; })()}</div>}
    </div>
  </div>
);

// Styled cover for a course card — uses channel color + seal character.
// If course.cover is a URL (starts with http, /uploads, or data:), render the
// uploaded image instead of the generated gradient. Legacy hero keys like
// "classics-hero" are NOT URLs and fall through to the pattern.
const CourseCover = ({ course, height = 160 }) => {
  const ch = (window.CHANNELS || []).find(c => c.id === course.channel);
  const th = (window.THEMES || {})[ch?.theme || 'zen'];
  const bg = th?.tokens['--accent-soft'] || 'var(--accent-soft)';
  const seal = th?.tokens['--accent'] || 'var(--accent)';
  const ink = th?.tokens['--ink'] || 'var(--ink)';

  const cover = (course && course.cover) || '';
  const isCoverUrl = /^(https?:|\/uploads\/|data:|\/)/.test(cover);

  if (isCoverUrl) {
    return (
      <div style={{
        position:'relative', height, width:'100%',
        borderRadius: 12, overflow:'hidden',
        background: bg,
      }}>
        <img
          src={cover}
          alt={course.title || ''}
          style={{
            position:'absolute', inset: 0,
            width:'100%', height:'100%',
            objectFit:'cover', display:'block',
          }}
          onError={e => { e.currentTarget.style.display = 'none'; }}
        />
        {/* Channel badge overlay — keeps the cover card feeling on-brand */}
        <div style={{
          position:'absolute', bottom: 10, left: 10,
          padding:'3px 8px', borderRadius: 4,
          fontSize: 10, letterSpacing:'0.1em', textTransform:'uppercase',
          color:'#fff', fontWeight: 500, fontFamily:'var(--font-sans)',
          background:'rgba(0,0,0,0.45)',
        }}>
          {ch?.name}
        </div>
      </div>
    );
  }

  // Repeated pattern backgrounds
  const patterns = {
    'ink-wash': `radial-gradient(ellipse at 30% 20%, rgba(138,25,25,0.08), transparent 60%),
                 radial-gradient(ellipse at 80% 70%, rgba(0,0,0,0.06), transparent 55%)`,
    petal:     `radial-gradient(circle at 20% 80%, rgba(181,72,104,0.12), transparent 45%),
                radial-gradient(circle at 80% 20%, rgba(201,166,106,0.10), transparent 45%)`,
    grid:      `linear-gradient(rgba(79,212,196,0.08) 1px, transparent 1px) 0 0/24px 24px,
                linear-gradient(90deg, rgba(79,212,196,0.08) 1px, transparent 1px) 0 0/24px 24px`,
    line:      `repeating-linear-gradient(90deg, transparent 0 40px, rgba(26,58,107,0.06) 40px 41px)`,
    mountain:  `radial-gradient(ellipse at 50% 110%, rgba(47,107,74,0.22), transparent 60%)`,
  };
  const pat = patterns[th?.pattern || 'ink-wash'];

  return (
    <div style={{
      position:'relative', height, width:'100%',
      background: bg,
      backgroundImage: pat,
      borderRadius: 12, overflow:'hidden',
      display:'flex', alignItems:'center', justifyContent:'center',
      fontFamily:'var(--font-serif)',
    }}>
      {/* Large ghost character */}
      <div style={{
        position:'absolute', right: -14, bottom: -20,
        fontSize: height * 1.0, lineHeight: 1, fontWeight: 700,
        color: seal, opacity: 0.18,
        fontFamily:'var(--font-serif)',
      }}>
        {course.seal || ch?.name?.[0] || 'Y'}
      </div>
      {/* Small seal */}
      <div style={{
        position:'absolute', top: 12, left: 12,
        width: 32, height: 32, background: seal,
        color:'#fff7ea', borderRadius: 3,
        display:'flex', alignItems:'center', justifyContent:'center',
        fontSize: 14, fontWeight: 700,
      }}>
        {course.seal || 'y'}
      </div>
      {/* Channel badge */}
      <div style={{
        position:'absolute', bottom: 12, left: 12,
        fontSize: 11, letterSpacing:'0.1em', textTransform:'uppercase',
        color: ink, fontWeight: 500, fontFamily:'var(--font-sans)',
      }}>
        {ch?.name}
      </div>
    </div>
  );
};

// Course card used in grids
const CourseCard = ({ course, onClick, compact = false }) => {
  const ch = (window.CHANNELS || []).find(c => c.id === course.channel);
  const lang = (typeof localStorage !== 'undefined' && (localStorage.getItem('yylearn.lang') || localStorage.getItem('yy.lang'))) || 'en';
  const isZh = lang && lang.startsWith('zh');
  return (
    <div
      onClick={onClick}
      className="card"
      style={{
        overflow:'hidden', cursor:'pointer', transition:'all 0.2s',
        display:'flex', flexDirection:'column',
      }}
      onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.borderColor = 'var(--ink-3)'; }}
      onMouseLeave={e => { e.currentTarget.style.transform = 'none'; e.currentTarget.style.borderColor = 'var(--line)'; }}
    >
      <CourseCover course={course} height={compact ? 120 : 160} />
      <div style={{ padding: compact ? 14 : 18, flex:1, display:'flex', flexDirection:'column' }}>
        <div style={{ fontSize: 11, color:'var(--ink-3)', letterSpacing:'0.08em', textTransform:'uppercase', marginBottom: 8 }}>
          {ch?.name}
        </div>
        <div style={{
          fontFamily:'var(--font-serif)', fontSize: compact ? 16 : 19,
          fontWeight:600, color:'var(--ink)', lineHeight:1.3, marginBottom: 6,
        }}>
          {course.title}
        </div>
        {!compact && course.instructor && (
          <div style={{ fontSize:13, color:'var(--ink-3)', marginBottom: 10 }}>
            {isZh ? '讲师 ' : 'by '}{typeof course.instructor === 'string' ? course.instructor : course.instructor.name}
          </div>
        )}
        <div style={{ display:'flex', alignItems:'center', gap: 12, fontSize: 12, color:'var(--ink-3)', marginTop:'auto' }}>
          <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}>
            <Icon name="clock" size={12} /> {course.hours}{isZh ? '小时' : 'h'}
          </span>
          <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}>
            <Icon name="video" size={12} /> {course.lessons} {isZh ? '节课' : 'lessons'}
          </span>
          {course.rating && (
            <span style={{ display:'inline-flex', alignItems:'center', gap:4, color:'var(--accent)' }}>
              <Icon name="star" size={12} /> {course.rating}
            </span>
          )}
        </div>
      </div>
    </div>
  );
};

// Segmented control
const Segmented = ({ options, value, onChange, style = {} }) => (
  <div style={{
    display:'inline-flex', padding: 3, borderRadius: 999,
    background:'var(--bg-alt)', border:'1px solid var(--line)',
    ...style,
  }}>
    {options.map(o => (
      <button
        key={o.value}
        onClick={() => onChange(o.value)}
        style={{
          padding:'6px 14px', borderRadius: 999, fontSize: 13, fontWeight: 500,
          background: value === o.value ? 'var(--surface)' : 'transparent',
          color: value === o.value ? 'var(--ink)' : 'var(--ink-3)',
          boxShadow: value === o.value ? '0 1px 3px rgba(0,0,0,0.06)' : 'none',
          transition: 'all 0.15s',
        }}
      >{o.label}</button>
    ))}
  </div>
);

// Progress bar
const Progress = ({ value, height = 4, color }) => (
  <div style={{ height, background:'var(--line)', borderRadius: 999, overflow:'hidden' }}>
    <div style={{
      width: `${value}%`, height:'100%',
      background: color || 'var(--accent)',
      transition:'width 0.3s',
    }} />
  </div>
);

// Toast — tiny notification that slides in
const useToast = () => {
  const [toasts, setToasts] = React.useState([]);
  const push = (msg, kind = 'info') => {
    const id = Date.now() + Math.random();
    setToasts(t => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3000);
  };
  const view = (
    <div style={{
      position:'fixed', top: 80, right: 16, zIndex: 9999,
      display:'flex', flexDirection:'column', gap: 8,
      pointerEvents:'none',
    }}>
      {toasts.map(t => (
        <div key={t.id} style={{
          padding:'10px 16px', background:'var(--ink)', color:'var(--bg)',
          borderRadius: 10, fontSize: 13, fontWeight: 500,
          boxShadow:'0 8px 24px rgba(0,0,0,0.15)',
          animation:'fadeIn 0.25s',
          pointerEvents:'auto',
        }}>{t.msg}</div>
      ))}
    </div>
  );
  return { push, view };
};

// Skeleton shimmer
const Skeleton = ({ w = '100%', h = 16, r = 6, style = {} }) => (
  <div style={{
    width: w, height: h, borderRadius: r,
    background:'linear-gradient(90deg, var(--line) 0%, var(--bg-alt) 50%, var(--line) 100%)',
    backgroundSize:'200% 100%',
    animation:'shimmer 1.4s infinite linear',
    ...style,
  }} />
);

// Section header — serif title + optional right-side action
const SectionHead = ({ title, sub, action }) => (
  <div style={{
    display:'flex', justifyContent:'space-between', alignItems:'flex-end',
    marginBottom: 20, gap: 16,
  }}>
    <div>
      <div style={{
        fontFamily:'var(--font-serif)', fontSize: 28, fontWeight:600,
        color:'var(--ink)', lineHeight: 1.15, letterSpacing:'-0.01em',
      }}>{title}</div>
      {sub && <div style={{ fontSize: 14, color:'var(--ink-3)', marginTop: 6 }}>{sub}</div>}
    </div>
    {action}
  </div>
);

// Avatar with initials or seal
const Avatar = ({ initials, size = 36, bg, color }) => (
  <div style={{
    width: size, height: size, borderRadius: '50%',
    background: bg || 'var(--ink-2)', color: color || 'var(--bg)',
    display:'inline-flex', alignItems:'center', justifyContent:'center',
    fontSize: size * 0.36, fontWeight: 600, letterSpacing:'-0.02em',
    flexShrink: 0,
  }}>{initials}</div>
);

// Format helpers
const fmtMin = m => `${Math.floor(m/60) ? Math.floor(m/60) + 'h ' : ''}${m%60}m`;
const fmtPrice = n => {
  if (n !== 0) return `$${n}`;
  const lang = (typeof localStorage !== 'undefined' && (localStorage.getItem('yylearn.lang') || localStorage.getItem('yy.lang'))) || 'en';
  return lang && lang.startsWith('zh') ? '免费' : 'Free';
};
const fmtNum = n => n >= 1000 ? (n/1000).toFixed(n >= 10000 ? 0 : 1) + 'k' : n;

// Shimmer keyframes
if (!document.getElementById('yy-shimmer-kf')) {
  const st = document.createElement('style');
  st.id = 'yy-shimmer-kf';
  st.textContent = `@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }`;
  document.head.appendChild(st);
}

// Header search — single component that renders BOTH the inline pill
// trigger (visible in the nav) and the full-screen overlay modal that opens
// on click / ⌘K. Drop into PrimaryNav with `<HeaderSearch lang={lang}/>`.
const HeaderSearch = ({ lang }) => {
  const T = (k) => (typeof t === 'function' ? t(k, lang) : k);
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const [results, setResults] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [active, setActive] = React.useState(0);
  const inputRef = React.useRef(null);

  // ⌘K / Ctrl+K opens; Esc closes.
  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
        e.preventDefault();
        setOpen(true);
      } else if (e.key === 'Escape' && open) {
        setOpen(false);
      }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open]);

  // Focus input when opened.
  React.useEffect(() => {
    if (open) {
      const id = setTimeout(() => { try { inputRef.current && inputRef.current.focus(); } catch(_){} }, 30);
      return () => clearTimeout(id);
    } else {
      setQ(''); setResults([]); setActive(0);
    }
  }, [open]);

  // Debounced live search — fetches /api/courses (cached server-side) and
  // filters client-side by title / subtitle / blurb / instructor name.
  React.useEffect(() => {
    if (!open) return;
    const needle = q.trim().toLowerCase();
    if (!needle) { setResults([]); return; }
    let cancelled = false;
    const tm = setTimeout(() => {
      setLoading(true);
      fetch('/api/courses?locale=' + encodeURIComponent(lang || 'en'), { credentials: 'same-origin' })
        .then(r => r.ok ? r.json() : { courses: [] })
        .then(j => {
          if (cancelled) return;
          const matches = (j.courses || []).filter(c => {
            const hay = [
              c.title, c.subtitle, c.blurb,
              (c.instructor && c.instructor.name) || '',
            ].filter(Boolean).join(' ').toLowerCase();
            return hay.includes(needle);
          }).slice(0, 12);
          setResults(matches);
          setActive(0);
        })
        .catch(() => {})
        .finally(() => { if (!cancelled) setLoading(false); });
    }, 180);
    return () => { cancelled = true; clearTimeout(tm); };
  }, [q, open, lang]);

  const go = (course) => {
    if (!course) return;
    location.href = '/course/' + encodeURIComponent(course.slug || course.id);
  };

  // Trigger pill — clickable button, replaces the old non-interactive div.
  const trigger = (
    <button
      type="button"
      className="nav-search"
      onClick={() => setOpen(true)}
      aria-label={T('nav.searchHint')}
      style={{
        display:'flex', alignItems:'center', gap: 6,
        padding:'6px 10px', borderRadius: 999, border:'1px solid var(--line)',
        fontSize: 12, color:'var(--ink-3)', cursor:'pointer',
        background:'var(--surface)',
      }}
    >
      <Icon name="search" size={14} />
      <span className="nav-search-text">{T('nav.searchHint')}</span>
      <span className="mono nav-search-key" style={{
        padding:'1px 5px', background:'var(--bg-alt)', borderRadius: 4, fontSize: 10,
      }}>⌘K</span>
    </button>
  );

  if (!open) return trigger;

  // Overlay modal portal
  const overlay = (
    <div
      onClick={(e) => { if (e.target === e.currentTarget) setOpen(false); }}
      style={{
        position:'fixed', inset: 0, zIndex: 1000,
        background:'rgba(15,12,8,0.55)',
        backdropFilter:'blur(6px)',
        display:'flex', justifyContent:'center', alignItems:'flex-start',
        paddingTop:'12vh', paddingLeft: 16, paddingRight: 16,
      }}
    >
      <div
        role="dialog"
        aria-label={T('nav.searchHint')}
        style={{
          width:'min(680px, 100%)',
          background:'var(--surface)',
          border:'1px solid var(--line)',
          borderRadius: 14,
          boxShadow:'0 24px 80px -20px rgba(0,0,0,0.45), 0 4px 16px rgba(0,0,0,0.08)',
          overflow:'hidden',
        }}
      >
        <div style={{
          display:'flex', alignItems:'center', gap: 12,
          padding:'14px 18px', borderBottom:'1px solid var(--line)',
        }}>
          <Icon name="search" size={18} />
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder={T('nav.searchHint')}
            onKeyDown={(e) => {
              if (e.key === 'ArrowDown') { e.preventDefault(); setActive(a => Math.min(a+1, results.length-1)); }
              else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(a => Math.max(a-1, 0)); }
              else if (e.key === 'Enter') {
                if (results[active]) go(results[active]);
              }
            }}
            style={{
              flex: 1, fontSize: 16, padding:'4px 0',
              border:'none', outline:'none', background:'transparent',
              color:'var(--ink)',
            }}
          />
          <kbd style={{
            fontFamily:'var(--font-mono)', fontSize: 11,
            padding:'2px 6px', borderRadius: 4,
            background:'var(--bg-alt)', color:'var(--ink-3)',
          }}>esc</kbd>
        </div>
        <div style={{ maxHeight:'60vh', overflowY:'auto' }}>
          {!q.trim() && (
            <div style={{ padding:'24px 18px', color:'var(--ink-3)', fontSize: 13 }}>
              {(lang && lang.startsWith('zh'))
                ? '输入课程标题、讲师、关键词…'
                : 'Type a course title, instructor, or keyword…'}
            </div>
          )}
          {q.trim() && loading && (
            <div style={{ padding:'24px 18px', color:'var(--ink-3)', fontSize: 13 }}>
              {(lang && lang.startsWith('zh')) ? '搜索中…' : 'Searching…'}
            </div>
          )}
          {q.trim() && !loading && results.length === 0 && (
            <div style={{ padding:'24px 18px', color:'var(--ink-3)', fontSize: 13 }}>
              {(lang && lang.startsWith('zh'))
                ? '没有找到匹配的课程。'
                : 'No matching courses.'}
            </div>
          )}
          {results.map((c, i) => (
            <button
              key={c.id}
              type="button"
              onClick={() => go(c)}
              onMouseEnter={() => setActive(i)}
              style={{
                width:'100%', textAlign:'left',
                display:'flex', alignItems:'center', gap: 12,
                padding:'10px 16px', cursor:'pointer',
                background: i === active ? 'var(--bg-alt)' : 'transparent',
                border:'none', borderBottom:'1px solid var(--line)',
                fontSize: 14, color:'var(--ink)',
              }}
            >
              <div style={{
                width: 56, height: 36, flex:'0 0 auto',
                borderRadius: 6, overflow:'hidden',
                background: c.cover ? `url(${c.cover}) center/cover` : 'var(--bg-alt)',
              }}/>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ fontFamily:'var(--font-serif)', fontWeight: 600,
                              whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
                  {c.title || ''}
                </div>
                <div style={{ fontSize: 12, color:'var(--ink-3)',
                              whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
                  {c.instructor && c.instructor.name ? c.instructor.name + ' · ' : ''}
                  {c.lessons || c.lessonCount || 0} {T('home.heroLessons')}
                </div>
              </div>
              <Icon name="arrowR" size={14} />
            </button>
          ))}
        </div>
      </div>
    </div>
  );

  return (
    <React.Fragment>
      {trigger}
      {overlay}
    </React.Fragment>
  );
};

Object.assign(window, {
  Placeholder, Logo, CourseCover, CourseCard, Segmented, Progress,
  useToast, Skeleton, SectionHead, Avatar,
  fmtMin, fmtPrice, fmtNum, HeaderSearch,
});
