import React, { useEffect, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  ADMIN_TOKEN_KEY,
  adminFetch,
  getApiBase,
  getFaviconUrlForSite,
  getTechstackIconUrl,
  techBadgeLabelColor,
  techBadgeSurfaceStyle,
} from './api';
import AdminModal from './components/admin/AdminModal';
import './AdminDashboard.css';

function parseTags(str) {
  if (!str || !String(str).trim()) return [];
  return String(str)
    .split(/[,，]/)
    .map((s) => s.trim())
    .filter(Boolean);
}

function truncateText(str, max) {
  if (!str) return '';
  const s = String(str);
  return s.length <= max ? s : `${s.slice(0, max)}…`;
}

function adminProjectListIconSrc(p) {
  try {
    if (p.link) new URL(p.link);
    else throw new Error();
    return getFaviconUrlForSite(p.link);
  } catch {
    return p.icon || 'https://api.iconify.design/mdi:web.svg';
  }
}

function handleAdminProjectIconError(e, p) {
  const img = e.currentTarget;
  if (!img) return;
  if (img.src.includes('favicon.smyhub.com') && p.icon) {
    img.src = p.icon;
    return;
  }
  img.src = 'https://api.iconify.design/mdi:web.svg';
}

function handleTechCardIconError(e) {
  const img = e.currentTarget;
  if (!img) return;
  img.onerror = null;
  img.src = 'https://api.iconify.design/mdi:help-circle-outline.svg';
}

function omitId(obj) {
  if (!obj || typeof obj !== 'object') return { ...obj };
  const { id: _i, ...rest } = obj;
  return rest;
}

/** 供原生取色器：仅纯色 #rgb/#rrggbb；渐变或其它内容时用占位色 */
function hexForNativeColorPicker(value) {
  const v = String(value || '').trim();
  if (!v || v.toLowerCase().includes('gradient')) return '#e8897a';
  const m3 = /^#([0-9a-f]{3})$/i.exec(v);
  if (m3) {
    const s = m3[1];
    return `#${s[0]}${s[0]}${s[1]}${s[1]}${s[2]}${s[2]}`.toLowerCase();
  }
  const m6 = /^#([0-9a-f]{6})$/i.exec(v);
  if (m6) return `#${m6[1].toLowerCase()}`;
  return '#e8897a';
}

const TECH_THEME_COLOR_SWATCHES = [
  '#000000',
  '#ffffff',
  '#3DDC84',
  '#4285F4',
  '#61DAFB',
  '#3776AB',
  '#2496ED',
  '#FF6600',
  '#646CFF',
  '#06B6D4',
  '#083FA5',
  '#E88939',
  '#68217A',
  '#F05032',
  '#00ADD8',
  '#ED8B00',
  '#47A248',
  '#DC382D',
  '#6DB33F',
  '#e8897a',
  '#1e293b',
  '#f472b6',
  '#a78bfa',
  '#f59e0b',
  '#14b8a6'
];

function projectBodyFromDraft(d) {
  return {
    title: d.title,
    description: d.description || '',
    link: d.link || '',
    icon: d.icon || '',
    tags: parseTags(d.tagsStr),
    techTags: Array.isArray(d.techTagNames) ? [...d.techTagNames] : [],
    admin: !!d.admin,
    show: d.show !== false,
    develop: !!d.develop
  };
}

const emptyProfileDraft = () => ({
  favicon: '',
  nickname: '',
  avatar: '',
  introduction: '',
  footer: '',
  site: '',
  homepage: ''
});

const emptyProjectDraft = () => ({
  title: '',
  description: '',
  link: '',
  icon: '',
  tagsStr: '',
  techTagNames: [],
  admin: false,
  show: true,
  develop: true
});

const emptyContactDraft = () => ({
  type: 'email',
  label: '',
  value: '',
  link: '',
  icon: '',
  show: true
});

const emptyTechDraft = () => ({
  name: '',
  link: '',
  svg: '',
  color: '#000000',
  show: true
});

export default function AdminDashboard() {
  const navigate = useNavigate();
  const [tab, setTab] = useState('profile');
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState('');
  const [msg, setMsg] = useState('');
  const [err, setErr] = useState('');
  const [savingDialog, setSavingDialog] = useState(false);
  const [savingTitle, setSavingTitle] = useState(false);
  const [profileForm, setProfileForm] = useState(emptyProfileDraft);
  const [techTitle, setTechTitle] = useState('技术栈');
  const [dialog, setDialog] = useState(null);

  const updateDraft = (patch) => {
    setDialog((d) => (d ? { ...d, draft: { ...d.draft, ...patch } } : null));
  };

  const refresh = useCallback(async () => {
    setLoading(true);
    setLoadError('');
    try {
      const r = await adminFetch('/all');
      if (r.status === 401) {
        sessionStorage.removeItem(ADMIN_TOKEN_KEY);
        navigate('/', { replace: true });
        setLoadError('未登录或会话已失效');
        setData(null);
        return;
      }
      if (!r.ok) {
        setLoadError(`加载失败（HTTP ${r.status}）`);
        setData(null);
        return;
      }
      setData(await r.json());
    } catch (e) {
      setLoadError(e.message || '网络错误');
      setData(null);
    } finally {
      setLoading(false);
    }
  }, [navigate]);

  const apiAdmin = async (path, options = {}) => {
    const res = await adminFetch(path, options);
    if (res.status === 401) {
      sessionStorage.removeItem(ADMIN_TOKEN_KEY);
      navigate('/', { replace: true });
      return null;
    }
    return res;
  };

  useEffect(() => {
    if (!sessionStorage.getItem(ADMIN_TOKEN_KEY)) {
      navigate('/', { replace: true });
      return;
    }
    refresh();
  }, [navigate, refresh]);

  useEffect(() => {
    if (data?.profile) {
      setProfileForm({
        favicon: data.profile.favicon || '',
        nickname: data.profile.nickname || '',
        avatar: data.profile.avatar || '',
        introduction: data.profile.introduction || '',
        footer: data.profile.footer || '',
        site: data.profile.site || '',
        homepage: data.profile.homepage || ''
      });
    }
    if (data?.techstack?.title) setTechTitle(data.techstack.title);
  }, [data]);

  const flashOk = (t) => {
    setErr('');
    setMsg(t);
    setTimeout(() => setMsg(''), 2500);
  };

  const flashErr = async (r) => {
    setMsg('');
    try {
      const j = await r.json();
      setErr(j.error || `请求失败 ${r.status}`);
    } catch {
      setErr(`请求失败 ${r.status}`);
    }
  };

  const closeDialog = () => setDialog(null);

  const saveDialog = async () => {
    if (!dialog || savingDialog) return;
    setSavingDialog(true);
    setErr('');
    try {
      const { kind, mode, id, draft } = dialog;
      if (kind === 'profile') {
        const r = await apiAdmin('/admin/profile', {
          method: 'PUT',
          body: JSON.stringify(draft)
        });
        if (!r) return;
        if (r.ok) {
          flashOk('资料已保存');
          closeDialog();
          refresh();
        } else await flashErr(r);
        return;
      }
      if (kind === 'project') {
        if (!draft.title?.trim()) {
          setErr('标题必填');
          return;
        }
        const body = projectBodyFromDraft(draft);
        if (mode === 'create') {
          const r = await apiAdmin('/admin/projects', {
            method: 'POST',
            body: JSON.stringify(body)
          });
          if (!r) return;
          if (r.ok) {
            flashOk('项目已添加');
            closeDialog();
            refresh();
          } else await flashErr(r);
        } else {
          const r = await apiAdmin(`/admin/projects/${id}`, {
            method: 'PUT',
            body: JSON.stringify(body)
          });
          if (!r) return;
          if (r.ok) {
            flashOk('项目已更新');
            closeDialog();
            refresh();
          } else await flashErr(r);
        }
        return;
      }
      if (kind === 'contact') {
        if (!draft.type?.trim()) {
          setErr('类型必填');
          return;
        }
        if (mode === 'create') {
          const r = await apiAdmin('/admin/contacts', {
            method: 'POST',
            body: JSON.stringify(draft)
          });
          if (!r) return;
          if (r.ok) {
            flashOk('联系方式已添加');
            closeDialog();
            refresh();
          } else await flashErr(r);
        } else {
          const r = await apiAdmin(`/admin/contacts/${id}`, {
            method: 'PUT',
            body: JSON.stringify(omitId(draft))
          });
          if (!r) return;
          if (r.ok) {
            flashOk('联系方式已更新');
            closeDialog();
            refresh();
          } else await flashErr(r);
        }
        return;
      }
      if (kind === 'tech') {
        if (!draft.name?.trim()) {
          setErr('名称必填');
          return;
        }
        if (mode === 'create') {
          const r = await apiAdmin('/admin/techstack/items', {
            method: 'POST',
            body: JSON.stringify(omitId(draft))
          });
          if (!r) return;
          if (r.ok) {
            flashOk('技术栈项已添加');
            closeDialog();
            refresh();
          } else await flashErr(r);
        } else {
          const r = await apiAdmin(`/admin/techstack/items/${id}`, {
            method: 'PUT',
            body: JSON.stringify(omitId(draft))
          });
          if (!r) return;
          if (r.ok) {
            flashOk('已更新');
            closeDialog();
            refresh();
          } else await flashErr(r);
        }
      }
    } finally {
      setSavingDialog(false);
    }
  };

  const saveTechTitle = async () => {
    if (savingTitle) return;
    setSavingTitle(true);
    setErr('');
    try {
      const r = await apiAdmin('/admin/techstack/title', {
        method: 'PUT',
        body: JSON.stringify({ title: techTitle })
      });
      if (!r) return;
      if (r.ok) {
        flashOk('板块标题已保存');
        refresh();
      } else await flashErr(r);
    } finally {
      setSavingTitle(false);
    }
  };

  const delProject = async (pid) => {
    if (!window.confirm('删除该项目？')) return;
    const r = await apiAdmin(`/admin/projects/${pid}`, { method: 'DELETE' });
    if (!r) return;
    if (r.ok) {
      flashOk('已删除');
      refresh();
    } else flashErr(r);
  };

  const delContact = async (cid) => {
    if (!window.confirm('删除该联系方式？')) return;
    const r = await apiAdmin(`/admin/contacts/${cid}`, { method: 'DELETE' });
    if (!r) return;
    if (r.ok) {
      flashOk('已删除');
      refresh();
    } else flashErr(r);
  };

  const delTech = async (tid) => {
    if (!window.confirm('删除该项？')) return;
    const r = await apiAdmin(`/admin/techstack/items/${tid}`, { method: 'DELETE' });
    if (!r) return;
    if (r.ok) {
      flashOk('已删除');
      refresh();
    } else flashErr(r);
  };

  const logout = () => {
    sessionStorage.removeItem(ADMIN_TOKEN_KEY);
    navigate('/');
  };

  const setTabAndClear = (k) => {
    setTab(k);
    setDialog(null);
    setErr('');
  };

  const dialogTitle =
    dialog &&
    ({
      profile: '编辑资料',
      project: dialog.mode === 'create' ? '添加项目' : '编辑项目',
      contact: dialog.mode === 'create' ? '添加联系方式' : '编辑联系方式',
      tech: dialog.mode === 'create' ? '添加技术栈项' : '编辑技术栈项'
    }[dialog.kind]);

  const renderDialogFields = () => {
    if (!dialog) return null;
    const { kind, draft } = dialog;
    if (kind === 'profile') {
      return (
        <div className="admin-form-grid">
          {[
            ['favicon', '网站图标 favicon URL'],
            ['nickname', '昵称'],
            ['avatar', '头像图片 URL'],
            ['introduction', '个人简介'],
            ['footer', '页脚版权文字'],
            ['site', '站点 URL（可选，用于 favicon 等）'],
            ['homepage', '主页链接（可选）']
          ].map(([field, label]) => (
            <React.Fragment key={field}>
              <label>{label}</label>
              {field === 'introduction' ? (
                <textarea
                  value={draft[field] || ''}
                  onChange={(e) => updateDraft({ [field]: e.target.value })}
                />
              ) : (
                <input
                  value={draft[field] || ''}
                  onChange={(e) => updateDraft({ [field]: e.target.value })}
                />
              )}
            </React.Fragment>
          ))}
        </div>
      );
    }
    if (kind === 'project') {
      const stackItems = data?.techstack?.items || [];
      const selected = new Set(draft.techTagNames || []);
      const toggleTech = (name) => {
        const cur = [...(draft.techTagNames || [])];
        const i = cur.indexOf(name);
        if (i >= 0) cur.splice(i, 1);
        else cur.push(name);
        updateDraft({ techTagNames: cur });
      };
      return (
        <>
          <div className="admin-form-grid">
            <label>标题</label>
            <input value={draft.title} onChange={(e) => updateDraft({ title: e.target.value })} />
            <label>描述</label>
            <textarea
              value={draft.description}
              onChange={(e) => updateDraft({ description: e.target.value })}
            />
            <label>项目链接</label>
            <input value={draft.link} onChange={(e) => updateDraft({ link: e.target.value })} />
            <label>项目图标</label>
            <p className="admin-field-hint">填写图片 URL（用于列表展示；可与 favicon 服务配合）。</p>
            <input value={draft.icon} onChange={(e) => updateDraft({ icon: e.target.value })} />
            <label>标签</label>
            <p className="admin-field-hint">业务/分类标签，多个用英文或中文逗号分隔。</p>
            <input
              value={draft.tagsStr}
              onChange={(e) => updateDraft({ tagsStr: e.target.value })}
            />
            <label>技术栈</label>
            <p className="admin-field-hint">
              点击选择已配置的技术栈（与「技术栈」页名称、图标一致）；需先在「技术栈」标签维护条目。
            </p>
            <div className="admin-tech-picker">
              {stackItems.length === 0 ? (
                <p className="admin-tech-picker-empty">暂无技术栈项，请先到「技术栈」添加。</p>
              ) : (
                stackItems.map((item) => {
                  const on = selected.has(item.name);
                  const url = getTechstackIconUrl(item);
                  return (
                    <button
                      key={item.id}
                      type="button"
                      className={`admin-tech-pick ${on ? 'admin-tech-pick--on' : ''}`}
                      onClick={() => toggleTech(item.name)}
                      title={item.name}
                      aria-pressed={on}
                    >
                      {url ? (
                        <img src={url} alt="" className="admin-tech-pick__img" loading="lazy" />
                      ) : (
                        <span className="admin-tech-pick__ph" aria-hidden>
                          ?
                        </span>
                      )}
                      <span className="admin-tech-pick__name">{item.name}</span>
                    </button>
                  );
                })
              )}
            </div>
            <div className="admin-check-row">
              <label className="admin-check">
                <input
                  type="checkbox"
                  checked={draft.admin}
                  onChange={(e) => updateDraft({ admin: e.target.checked })}
                />
                仅管理员可见
              </label>
              <label className="admin-check">
                <input
                  type="checkbox"
                  checked={draft.show}
                  onChange={(e) => updateDraft({ show: e.target.checked })}
                />
                在首页展示
              </label>
              <label className="admin-check">
                <input
                  type="checkbox"
                  checked={draft.develop}
                  onChange={(e) => updateDraft({ develop: e.target.checked })}
                />
                自制项目
              </label>
            </div>
          </div>
        </>
      );
    }
    if (kind === 'contact') {
      return (
        <div className="admin-form-grid">
          <label>类型</label>
          <p className="admin-field-hint">如 email、github、personprofile 等。</p>
          <input value={draft.type} onChange={(e) => updateDraft({ type: e.target.value })} />
          <label>显示名称</label>
          <input value={draft.label} onChange={(e) => updateDraft({ label: e.target.value })} />
          <label>值 / 账号</label>
          <input value={draft.value} onChange={(e) => updateDraft({ value: e.target.value })} />
          <label>跳转链接</label>
          <input value={draft.link} onChange={(e) => updateDraft({ link: e.target.value })} />
          <label>图标</label>
          <p className="admin-field-hint">图片 URL 或 emoji（由前台解析）。</p>
          <input value={draft.icon} onChange={(e) => updateDraft({ icon: e.target.value })} />
          <label className="admin-check">
            <input
              type="checkbox"
              checked={draft.show !== false}
              onChange={(e) => updateDraft({ show: e.target.checked })}
            />
            在首页展示联系方式（关闭则前台访客看不到本条）
          </label>
        </div>
      );
    }
    if (kind === 'tech') {
      return (
        <div className="admin-form-grid">
          <label>技术名称</label>
          <input value={draft.name} onChange={(e) => updateDraft({ name: e.target.value })} />
          <label>官网或文档链接</label>
          <p className="admin-field-hint">点击徽章跳转用；可选。</p>
          <input
            value={draft.link}
            onChange={(e) => updateDraft({ link: e.target.value })}
            placeholder="https://example.com"
          />
          <label>图标 SVG</label>
          <p className="admin-field-hint">
            填 data/logo 下文件名（如 react.svg）或 SVG 完整外链（https://…/xx.svg）。不要填到上一项「链接」里。
          </p>
          <input
            value={draft.svg}
            onChange={(e) => updateDraft({ svg: e.target.value })}
            placeholder="react.svg 或 https://…/icon.svg"
          />
          <label>主题色</label>
          <p className="admin-field-hint">
            纯色用 #hex；也可用 CSS{' '}
            <code style={{ fontSize: '11px' }}>linear-gradient(...)</code>。取色盘与快捷色会写入纯色并覆盖渐变。
          </p>
          <div className="admin-color-row">
            <input
              type="color"
              className="admin-color-native"
              value={hexForNativeColorPicker(draft.color)}
              onChange={(e) => updateDraft({ color: e.target.value })}
              title="调色盘"
              aria-label="打开调色盘选择颜色"
            />
            <input
              value={draft.color || ''}
              onChange={(e) => updateDraft({ color: e.target.value })}
              className="admin-color-hex-input"
              placeholder="#3DDC84 或 linear-gradient(...)"
              spellCheck={false}
            />
          </div>
          <div className="admin-color-swatches" role="group" aria-label="常用颜色">
            {TECH_THEME_COLOR_SWATCHES.map((hex) => (
              <button
                key={hex}
                type="button"
                className={`admin-color-swatch ${hex.toLowerCase() === '#ffffff' ? 'admin-color-swatch--light' : ''}`}
                style={{ backgroundColor: hex }}
                title={hex}
                aria-label={`使用 ${hex}`}
                onClick={() => updateDraft({ color: hex })}
              />
            ))}
          </div>
          <label className="admin-check">
            <input
              type="checkbox"
              checked={draft.show}
              onChange={(e) => updateDraft({ show: e.target.checked })}
            />
            在首页展示
          </label>
        </div>
      );
    }
    return null;
  };

  if (loading && !data && !loadError) {
    return (
      <div className="admin-page">
        <p style={{ textAlign: 'center', padding: 40 }}>加载中…</p>
      </div>
    );
  }

  if (loadError && !data) {
    return (
      <div className="admin-page">
        <div className="admin-panel" style={{ textAlign: 'center' }}>
          <p className="admin-msg err">{loadError}</p>
          <button type="button" className="admin-toolbar-btn primary" onClick={refresh}>
            重试加载
          </button>
        </div>
      </div>
    );
  }

  if (!data) {
    return null;
  }

  const projects = data.projects?.projects || [];
  const contacts = data.contacts?.contacts || [];
  const techItems = data.techstack?.items || [];

  return (
    <div className="admin-page">
      <div className="admin-top">
        <h1>萌芽主页 · 管理</h1>
        <div className="admin-top-actions">
          <button type="button" onClick={() => navigate('/')}>
            返回首页
          </button>
          <button type="button" onClick={refresh} disabled={loading}>
            刷新数据
          </button>
          <button type="button" className="danger" onClick={logout}>
            退出登录
          </button>
        </div>
      </div>

      <div className="admin-tabs">
        {['profile', 'projects', 'contacts', 'tech'].map((k) => (
          <button
            key={k}
            type="button"
            className={tab === k ? 'active' : ''}
            onClick={() => setTabAndClear(k)}
          >
            {k === 'profile' && '资料'}
            {k === 'projects' && '项目'}
            {k === 'contacts' && '联系方式'}
            {k === 'tech' && '技术栈'}
          </button>
        ))}
      </div>

      <div className="admin-panel">
        {msg ? <p className="admin-msg">{msg}</p> : null}
        {err ? <p className="admin-msg err">{err}</p> : null}

        {tab === 'profile' && (
          <div>
            <div className="admin-summary">
              <p>
                <strong>{profileForm.nickname || '（未设置昵称）'}</strong>
              </p>
              <p className="admin-summary-meta">
                {profileForm.footer || '（未设置页脚）'}
              </p>
            </div>
            <button
              type="button"
              className="admin-toolbar-btn primary"
              onClick={() =>
                setDialog({ kind: 'profile', mode: 'edit', draft: { ...profileForm } })
              }
            >
              编辑资料
            </button>
          </div>
        )}

        {tab === 'projects' && (
          <div>
            <div className="admin-toolbar">
              <button
                type="button"
                className="admin-toolbar-btn primary"
                onClick={() =>
                  setDialog({ kind: 'project', mode: 'create', draft: emptyProjectDraft() })
                }
              >
                添加项目
              </button>
            </div>
            <h3 className="admin-list-heading">已有项目</h3>
            {projects.map((p) => (
              <div key={p.id} className="admin-row admin-project-card">
                <div className="admin-project-card__icon-wrap">
                  <img
                    className="admin-project-card__icon"
                    src={adminProjectListIconSrc(p)}
                    alt=""
                    loading="lazy"
                    onError={(e) => handleAdminProjectIconError(e, p)}
                  />
                </div>
                <div className="admin-project-card__main">
                  <div className="admin-project-card__head">
                    <strong className="admin-project-card__title">{p.title}</strong>
                    <div className="admin-row-btns">
                      <button
                        type="button"
                        onClick={() =>
                          setDialog({
                            kind: 'project',
                            mode: 'edit',
                            id: p.id,
                            draft: {
                              title: p.title,
                              description: p.description,
                              link: p.link,
                              icon: p.icon,
                              tagsStr: (p.tags || []).join(', '),
                              techTagNames: [...(p.techTags || [])],
                              admin: p.admin,
                              show: p.show,
                              develop: p.develop
                            }
                          })
                        }
                      >
                        编辑
                      </button>
                      <button type="button" className="warn" onClick={() => delProject(p.id)}>
                        删除
                      </button>
                    </div>
                  </div>
                  {p.description ? (
                    <p className="admin-project-card__desc">{truncateText(p.description, 120)}</p>
                  ) : null}
                  {p.link ? (
                    <a
                      className="admin-project-card__link"
                      href={p.link}
                      target="_blank"
                      rel="noopener noreferrer"
                    >
                      {truncateText(p.link, 72)}
                    </a>
                  ) : (
                    <span className="admin-project-card__link admin-project-card__link--empty">
                      未设置链接
                    </span>
                  )}
                  <div className="admin-project-card__meta">
                    {(p.tags || []).slice(0, 6).map((tag, idx) => (
                      <span key={`${p.id}-c${idx}`} className="admin-project-card__tag">
                        {tag}
                      </span>
                    ))}
                    {(p.tags || []).length > 6 ? (
                      <span className="admin-project-card__tag admin-project-card__tag--more">
                        +{(p.tags || []).length - 6} 标签
                      </span>
                    ) : null}
                    {(p.techTags || []).slice(0, 10).map((tagName, idx) => {
                      const titem = techItems.find((t) => t.name === tagName);
                      const url = titem ? getTechstackIconUrl(titem) : null;
                      const paint = titem?.color || '#94a3b8';
                      if (url) {
                        return (
                          <span
                            key={`${p.id}-k${idx}`}
                            className="admin-project-card__tech-ico"
                            style={techBadgeSurfaceStyle(paint)}
                            title={tagName}
                          >
                            <img src={url} alt="" loading="lazy" />
                          </span>
                        );
                      }
                      return (
                        <span
                          key={`${p.id}-k${idx}`}
                          className="admin-project-card__tag admin-project-card__tag--tech"
                        >
                          {tagName}
                        </span>
                      );
                    })}
                    {(p.techTags || []).length > 10 ? (
                      <span className="admin-project-card__tag admin-project-card__tag--more">
                        +{(p.techTags || []).length - 10} 技术
                      </span>
                    ) : null}
                    {p.develop ? (
                      <span className="admin-project-card__pill admin-project-card__pill--dev">
                        自制
                      </span>
                    ) : (
                      <span className="admin-project-card__pill admin-project-card__pill--deploy">
                        部署
                      </span>
                    )}
                    {p.admin ? (
                      <span className="admin-project-card__pill admin-project-card__pill--admin">
                        仅管理员
                      </span>
                    ) : null}
                    {p.show === false ? (
                      <span className="admin-project-card__pill admin-project-card__pill--hidden">
                        首页隐藏
                      </span>
                    ) : null}
                  </div>
                </div>
              </div>
            ))}
          </div>
        )}

        {tab === 'contacts' && (
          <div>
            <div className="admin-toolbar">
              <button
                type="button"
                className="admin-toolbar-btn primary"
                onClick={() =>
                  setDialog({ kind: 'contact', mode: 'create', draft: emptyContactDraft() })
                }
              >
                添加联系方式
              </button>
            </div>
            <h3 className="admin-list-heading">已有记录</h3>
            {contacts.map((c) => (
              <div key={c.id} className="admin-row">
                <div className="admin-row-head">
                  <strong>
                    {c.label} ({c.type})
                  </strong>
                  <div className="admin-row-btns">
                    <button
                      type="button"
                      onClick={() =>
                        setDialog({
                          kind: 'contact',
                          mode: 'edit',
                          id: c.id,
                          draft: {
                            type: c.type,
                            label: c.label || '',
                            value: c.value || '',
                            link: c.link || '',
                            icon: c.icon || '',
                            show: c.show !== false,
                          }
                        })
                      }
                    >
                      编辑
                    </button>
                    <button type="button" className="warn" onClick={() => delContact(c.id)}>
                      删除
                    </button>
                  </div>
                </div>
                <div className="admin-row-sub">{c.value}</div>
                {c.show === false ? (
                  <div style={{ marginTop: '8px' }}>
                    <span className="admin-project-card__pill admin-project-card__pill--hidden">
                      首页隐藏
                    </span>
                  </div>
                ) : null}
              </div>
            ))}
          </div>
        )}

        {tab === 'tech' && (
          <div>
            <div className="admin-form-grid admin-inline-title">
              <label>板块标题</label>
              <div className="admin-title-row">
                <input value={techTitle} onChange={(e) => setTechTitle(e.target.value)} />
                <button
                  type="button"
                  className="admin-toolbar-btn primary"
                  onClick={saveTechTitle}
                  disabled={savingTitle}
                >
                  {savingTitle ? '保存中…' : '保存标题'}
                </button>
              </div>
            </div>
            <div className="admin-toolbar">
              <button
                type="button"
                className="admin-toolbar-btn primary"
                onClick={() =>
                  setDialog({ kind: 'tech', mode: 'create', draft: emptyTechDraft() })
                }
              >
                添加技术栈项
              </button>
            </div>
            <h3 className="admin-list-heading">已有项</h3>
            <div className="admin-tech-grid">
              {techItems.map((t) => {
                const iconUrl = getTechstackIconUrl(t);
                const paint = t.color || '#555555';
                return (
                <div key={t.id} className="admin-tech-card">
                  <div className="admin-row-head">
                    <span
                      className="admin-project-card__tech-ico admin-tech-card__ico"
                      style={techBadgeSurfaceStyle(paint)}
                      title={t.name}
                    >
                      {iconUrl ? (
                        <img
                          src={iconUrl}
                          alt=""
                          loading="lazy"
                          onError={handleTechCardIconError}
                        />
                      ) : (
                        <span
                          className="admin-tech-card__ph"
                          style={{ color: techBadgeLabelColor(paint) }}
                          aria-hidden
                        >
                          ?
                        </span>
                      )}
                    </span>
                    <strong className="admin-tech-card__name" title={t.name}>
                      {t.name}
                    </strong>
                    <div className="admin-row-btns">
                      <button
                        type="button"
                        onClick={() =>
                          setDialog({
                            kind: 'tech',
                            mode: 'edit',
                            id: t.id,
                            draft: { ...t }
                          })
                        }
                      >
                        编辑
                      </button>
                      <button type="button" className="warn" onClick={() => delTech(t.id)}>
                        删除
                      </button>
                    </div>
                  </div>
                </div>
              );
              })}
            </div>
          </div>
        )}
      </div>

      <AdminModal
        open={!!dialog}
        title={dialogTitle || ''}
        panelClassName={dialog?.kind === 'project' ? 'admin-modal-panel--wide' : undefined}
        onClose={closeDialog}
        footer={
          <>
            <button type="button" className="secondary" onClick={closeDialog} disabled={savingDialog}>
              取消
            </button>
            <button type="button" className="primary" onClick={saveDialog} disabled={savingDialog}>
              {savingDialog ? '保存中…' : '保存'}
            </button>
          </>
        }
      >
        {dialog ? renderDialogFields() : null}
      </AdminModal>
    </div>
  );
}
