/* ===== Screens Part 2: Strategy (AI chat), Creators ===== */

// ========== 03 · CAMPAIGN STRATEGY ==========
const campaignApiUrl = (pathname) => (window.location.protocol === 'file:' ? `http://localhost:4173${pathname}` : pathname);
const campaignListValue = (value) => Array.isArray(value)
  ? value.join('、')
  : String(value || '');
const campaignSplitValue = (value) => String(value || '').split(/[、,，;；\n]/).map(item => item.trim()).filter(Boolean);
const campaignNumber = (value) => Number(value || 0);
const campaignViews = (post = {}) => campaignNumber(post.views || post.plays || post.reads || post.organic_impressions);
const campaignInteraction = (post = {}) => campaignNumber(post.likes) + campaignNumber(post.comments) + campaignNumber(post.shares) + campaignNumber(post.saves);
const campaignCompact = (value) => {
  const n = campaignNumber(value);
  if (n >= 100000000) return `${(n / 100000000).toFixed(1)}亿`;
  if (n >= 10000) return `${(n / 10000).toFixed(1)}万`;
  return String(Math.round(n));
};
const campaignPostIds = (post = {}) => [post.post_id, post.content_id, post.id].filter(Boolean);
const campaignPostBriefIds = (post = {}) => [
  post.primaryMasterBriefId,
  post.briefMapping?.primaryMasterBriefId,
  ...(post.masterBriefIds || []),
  ...(post.briefMapping?.masterBriefIds || []),
].filter(Boolean);
const campaignMatchesPost = (campaign = {}, post = {}) => {
  const ids = new Set(campaign.assetPostIds || []);
  if (campaignPostIds(post).some(id => ids.has(id))) return true;
  if (ids.size) return false;
  const briefIds = new Set(campaignPostBriefIds(post));
  if (campaign.id && briefIds.has(campaign.id)) return true;
  const parentIds = [post.parentCampaignId, post.campaignId, post.briefMapping?.parentCampaignId].filter(Boolean);
  if (campaign.id && parentIds.includes(campaign.id)) return true;
  if (ids.size || briefIds.size || parentIds.length) return false;
  const labels = campaign.assetCampaignLabels?.length ? campaign.assetCampaignLabels : [campaign.name];
  return labels.some(label => label && [post.campaign, post.product, post.content_type, post.title, post.caption].join(' ').includes(label));
};
const INITIATIVE_TYPE_META = {
  campaign: { label: 'Campaign', text: '阶段性活动', tone: 'accent' },
  pillar: { label: 'Pillar', text: '长期内容支柱', tone: 'info' },
  series: { label: 'Series', text: '固定栏目', tone: 'info' },
  standalone: { label: 'Content', text: '单篇/内容任务', tone: 'ghost' },
  social_care: { label: 'Care', text: '社群照护', tone: 'warn' },
};
const SOCIAL_STRATEGY_OBJECTS = [
  {
    type: 'pillar',
    title: 'Content Pillars',
    cn: '长期内容支柱',
    examples: [
      { en: 'Product Education', cn: '产品教育' },
      { en: 'Technology & Innovation', cn: '技术与创新' },
      { en: 'Ownership Experience', cn: '车主体验' },
      { en: 'Lifestyle & Use Cases', cn: '生活方式与使用场景' },
    ],
  },
  {
    type: 'series',
    title: 'Content Series',
    cn: '固定栏目',
    examples: [
      { en: 'Feature Friday', cn: '周五功能讲解' },
      { en: 'EV Myth Busters', cn: '电动车误区破解' },
      { en: 'Owner’s Garage', cn: '车主车库' },
      { en: 'Test Drive Tuesdays', cn: '周二试驾日' },
    ],
  },
  {
    type: 'campaign',
    title: 'Social Media Campaigns',
    cn: '阶段性 Campaign',
    examples: [
      { en: 'New Model Launch', cn: '新车型发布' },
      { en: 'Auto Show', cn: '车展活动' },
      { en: 'Creator Collaboration', cn: '创作者合作' },
      { en: 'UGC Challenge', cn: '用户共创挑战' },
    ],
  },
  {
    type: 'social_care',
    title: 'Community / Social Care',
    cn: '社群响应',
    examples: [
      { en: 'Comment Response', cn: '评论回复' },
      { en: 'DM Handling', cn: '私信处理' },
      { en: 'Lead Routing', cn: '线索分发' },
      { en: 'Crisis Response', cn: '危机响应' },
    ],
  },
  {
    type: 'standalone',
    title: 'Standalone Content',
    cn: '单篇内容任务',
    examples: [
      { en: 'Model Introduction', cn: '车型介绍' },
      { en: 'Feature Explainer', cn: '功能说明' },
      { en: 'Service Guide', cn: '服务说明' },
      { en: 'Brand Culture Story', cn: '品牌文化长文' },
    ],
  },
];
const campaignAppendUniqueText = (current = '', additions = []) => {
  const values = campaignSplitValue(current);
  additions.filter(Boolean).forEach(item => {
    if (!values.includes(item)) values.push(item);
  });
  return values.join('、');
};
const isOpenMarketHandoff = item => !['consumed', 'archived'].includes(item.status);
const DEFAULT_STRATEGY_THESIS = {
  title: 'MONTX 2026-2027 预热策略母题',
  timeframe: '2026-2027',
  coreThesis: '不要急着卖车，先卖一种判断。',
  marketBelief: '未来的车不应该只是更智能、更大、更快，而应该更懂真实生活、更可信、更值得等待。',
  contentParadigm: ['问题定义', '用户洞察', '品牌原则', '设计取舍', '研发证据', '场景想象', '社群共创', '车型期待', '上市转化'],
  guardrails: ['不急于销售转化', '不把概念车写成确定量产承诺', '不堆参数，不做空泛科技感', '优先建立可信判断和长期期待'],
};
const STRATEGY_STAGE_HINTS = {
  问题定义: '用外部痛点和行业错位定义“为什么需要另一种车”。',
  用户洞察: '把真实生活、移动办公、户外、自驾、共创用户的具体矛盾讲清楚。',
  品牌原则: '解释 MONTX 做判断的原则，不急着堆卖点。',
  设计取舍: '讲清楚为什么这样设计、放弃了什么、坚持了什么。',
  研发证据: '用测试、工程、材料、原型和团队证据建立可信度。',
  场景想象: '把产品放进真实生活，而不是只放进参数表。',
  社群共创: '让用户参与定义车、场景、功能和表达。',
  车型期待: '逐步建立 P10 / V10 等车型期待，但保持概念边界。',
  上市转化: '最后才进入预约、试驾、线索和销售转化。',
};
const strategyText = value => Array.isArray(value) ? value.join('、') : String(value || '');
const strategySplitList = value => String(value || '').split(/[、;；\n]/).map(item => item.trim()).filter(Boolean);
const strategyInferStage = (initiative = {}) => {
  const text = [initiative.name, initiative.stage, initiative.coreMessage, ...(initiative.goals || []), ...(initiative.sellingPoints || [])].join(' ');
  if (/牛马|共创|creator|ugc|kol|达人/i.test(text)) return '社群共创';
  if (/车展|邀请|探访|发布会|prelaunch|预发布|首发|launch/i.test(text)) return '车型期待';
  if (/tvc|品牌|slogan|principle|culture/i.test(text)) return '品牌原则';
  if (/研发|测试|证据|工程|技术|innovation|feature/i.test(text)) return '研发证据';
  if (/场景|生活|数字游民|户外|移动办公|use case|lifestyle/i.test(text)) return '场景想象';
  if (/车型|产品介绍|外观|功能|设计|取舍|model|product/i.test(text)) return '设计取舍';
  if (/洞察|用户|人群|痛点/i.test(text)) return '用户洞察';
  if ((initiative.type || '') === 'campaign') return '车型期待';
  return '问题定义';
};
const buildStrategySimulation = (thesis = DEFAULT_STRATEGY_THESIS, initiatives = []) => {
  const stages = thesis.contentParadigm?.length ? thesis.contentParadigm : DEFAULT_STRATEGY_THESIS.contentParadigm;
  return stages.map(stage => {
    const matched = initiatives.filter(item => strategyInferStage(item) === stage);
    return {
      stage,
      hint: STRATEGY_STAGE_HINTS[stage] || '作为内容创作策略的一段叙事推进。',
      objects: matched.slice(0, 4),
      status: matched.length ? 'covered' : 'gap',
      recommendation: matched.length
        ? `已有 ${matched.length} 个策略对象可承接，下一步把 brief 改写成“${stage}”视角。`
        : `缺少“${stage}”内容，需要新增 pillar / series / standalone brief。`,
    };
  });
};

const Strategy = ({ workspaceContext }) => {
  const brandStoreReady = Boolean(window.BrandMemoryStore?.useBrandMemoryProfiles);
  const brandState = brandStoreReady ? window.BrandMemoryStore.useBrandMemoryProfiles() : { brands: [], setBrands: () => {}, ready: false };
  const brandId = workspaceContext?.brandId || 'montx';
  const brand = brandStoreReady ? window.BrandMemoryStore.getBrandProfile(brandId, brandState.brands) : null;
  const [campaigns, setCampaigns] = React.useState([]);
  const [activeId, setActiveId] = React.useState('');
  const [form, setForm] = React.useState(null);
  const [social, setSocial] = React.useState({ posts: [], comments: [], status: 'idle' });
  const [marketHandoffs, setMarketHandoffs] = React.useState([]);
  const [strategyThesis, setStrategyThesis] = React.useState(DEFAULT_STRATEGY_THESIS);
  const [thesisStatus, setThesisStatus] = React.useState('');
  const [saving, setSaving] = React.useState('');
  const [writeStatus, setWriteStatus] = React.useState('');

  const loadCampaigns = React.useCallback(async () => {
    const response = await fetch(campaignApiUrl('/api/initiatives'));
    const data = await response.json();
    const rows = data.initiatives || data.campaigns || [];
    setCampaigns(rows);
    setActiveId(current => current || rows[0]?.id || '');
  }, []);

  const loadSocial = React.useCallback(async () => {
    try {
      const response = await fetch(campaignApiUrl('/api/social/sync/status'));
      const data = await response.json();
      setSocial({ posts: data.posts || [], comments: data.comments || [], status: data.status || 'idle' });
    } catch {
      setSocial({ posts: [], comments: [], status: 'offline' });
    }
  }, []);

  const loadMarketHandoffs = React.useCallback(async () => {
    try {
      const response = await fetch(campaignApiUrl('/api/market/handoffs?target_module=strategy&limit=10'));
      const data = await response.json();
      if (response.ok) setMarketHandoffs((data.handoffs || []).filter(isOpenMarketHandoff));
    } catch {
      setMarketHandoffs([]);
    }
  }, []);

  const loadStrategyThesis = React.useCallback(async () => {
    try {
      const response = await fetch(campaignApiUrl('/api/strategy/thesis'));
      const data = await response.json();
      if (response.ok && data.strategyThesis) setStrategyThesis({
        ...DEFAULT_STRATEGY_THESIS,
        ...data.strategyThesis,
        contentParadigmText: strategyText(data.strategyThesis.contentParadigm),
        guardrailsText: strategyText(data.strategyThesis.guardrails),
      });
    } catch {
      setStrategyThesis({
        ...DEFAULT_STRATEGY_THESIS,
        contentParadigmText: strategyText(DEFAULT_STRATEGY_THESIS.contentParadigm),
        guardrailsText: strategyText(DEFAULT_STRATEGY_THESIS.guardrails),
      });
    }
  }, []);

  React.useEffect(() => { loadCampaigns(); loadSocial(); loadMarketHandoffs(); loadStrategyThesis(); }, [loadCampaigns, loadSocial, loadMarketHandoffs, loadStrategyThesis]);

  const activeCampaign = campaigns.find(item => item.id === activeId) || campaigns[0] || null;
  React.useEffect(() => {
    if (activeCampaign) setForm({
      ...activeCampaign,
      goalsText: campaignListValue(activeCampaign.goals),
      platformFocusText: campaignListValue(activeCampaign.platformFocus),
      audienceText: campaignListValue(activeCampaign.audience),
      sellingPointsText: campaignListValue(activeCampaign.sellingPoints),
      contentBoundariesText: campaignListValue(activeCampaign.contentBoundaries),
      riskBoundariesText: campaignListValue(activeCampaign.riskBoundaries),
      assetCampaignLabelsText: campaignListValue(activeCampaign.assetCampaignLabels),
      type: activeCampaign.type || 'campaign',
    });
  }, [activeCampaign?.id]);

  const linkedPosts = React.useMemo(() => (social.posts || []).filter(post => campaignMatchesPost(activeCampaign || {}, post)), [social.posts, activeCampaign]);
  const commentsByPost = React.useMemo(() => {
    const map = new Map();
    (social.comments || []).forEach(comment => {
      const list = map.get(comment.post_id) || [];
      list.push(comment);
      map.set(comment.post_id, list);
    });
    return map;
  }, [social.comments]);
  const linkedCommentCount = linkedPosts.reduce((sum, post) => sum + Math.max(campaignNumber(post.comments), (commentsByPost.get(post.post_id) || []).length), 0);
  const metrics = {
    posts: linkedPosts.length,
    views: linkedPosts.reduce((sum, post) => sum + campaignViews(post), 0),
    interaction: linkedPosts.reduce((sum, post) => sum + campaignInteraction(post), 0),
    comments: linkedCommentCount,
  };
  const topPosts = [...linkedPosts].sort((a, b) => campaignInteraction(b) - campaignInteraction(a) || campaignViews(b) - campaignViews(a)).slice(0, 6);

  const patchForm = (key, value) => setForm(prev => ({ ...(prev || {}), [key]: value }));
  const patchStrategyThesis = (key, value) => setStrategyThesis(prev => ({ ...(prev || DEFAULT_STRATEGY_THESIS), [key]: value }));
  const saveStrategyThesis = async () => {
    setThesisStatus('saving');
    const payload = {
      ...strategyThesis,
      contentParadigm: strategySplitList(strategyThesis.contentParadigmText || strategyText(strategyThesis.contentParadigm)),
      guardrails: strategySplitList(strategyThesis.guardrailsText || strategyText(strategyThesis.guardrails)),
      source: strategyThesis.source || 'market_sensing_session',
    };
    const response = await fetch(campaignApiUrl('/api/strategy/thesis'), {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ strategyThesis: payload }),
    });
    const data = await response.json();
    if (response.ok && data.strategyThesis) {
      setStrategyThesis({
        ...data.strategyThesis,
        contentParadigmText: strategyText(data.strategyThesis.contentParadigm),
        guardrailsText: strategyText(data.strategyThesis.guardrails),
      });
      setThesisStatus('saved');
      setTimeout(() => setThesisStatus(''), 1200);
    } else {
      setThesisStatus('error');
    }
  };
  const applyThesisToCurrentObject = () => {
    const guardrails = strategySplitList(strategyThesis.guardrailsText || strategyText(strategyThesis.guardrails));
    const stage = strategyInferStage(form || activeCampaign || {});
    setForm(prev => ({
      ...(prev || {}),
      coreMessage: prev?.coreMessage || strategyThesis.marketBelief || strategyThesis.coreThesis,
      contentBoundariesText: campaignAppendUniqueText(prev?.contentBoundariesText, [
        `策略母题：${strategyThesis.coreThesis}`,
        `内容阶段：${stage}`,
        STRATEGY_STAGE_HINTS[stage],
      ]),
      riskBoundariesText: campaignAppendUniqueText(prev?.riskBoundariesText, guardrails),
    }));
    setThesisStatus('applied');
    setTimeout(() => setThesisStatus(''), 1200);
  };
  const applyMarketHandoff = async (handoff = marketHandoffs[0]) => {
    const pack = handoff?.contextPack || {};
    const snapshot = pack.market_snapshot || {};
    const benchmark = pack.competitive_benchmark || {};
    const riskNotes = pack.risk_notes || [];
    const activeSignals = snapshot.active_signals || [];
    const signalActions = activeSignals.map(item => item.recommended_action).filter(Boolean).slice(0, 3);
    const riskBoundaries = riskNotes.flatMap(item => item.review_reason || []).map(item => `市场感知复核：${item}`);
    const contentBoundaries = [
      ...(benchmark.white_spaces || []).map(item => `市场空位：${item}`),
      ...signalActions,
    ];
    setForm(prev => ({
      ...(prev || {}),
      coreMessage: prev?.coreMessage || snapshot.summary || prev?.masterBrief?.coreMessage || '',
      contentBoundariesText: campaignAppendUniqueText(prev?.contentBoundariesText, contentBoundaries),
      riskBoundariesText: campaignAppendUniqueText(prev?.riskBoundariesText, riskBoundaries),
      marketContextPackId: handoff?.context_pack_id || '',
    }));
    setWriteStatus(`已应用 Market Context：${handoff?.context_pack_id || 'context-pack'}`);
    if (handoff?.handoff_id) {
      await fetch(campaignApiUrl(`/api/market/handoffs/${encodeURIComponent(handoff.handoff_id)}/status`), {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ status: 'consumed', consumed_by: form?.id || activeCampaign?.id || 'strategy-workbench', note: 'Applied to strategy object form' }),
      }).catch(() => {});
      loadMarketHandoffs();
    }
  };
  const formPayload = () => ({
    ...form,
    goals: campaignSplitValue(form.goalsText),
    platformFocus: campaignSplitValue(form.platformFocusText),
    audience: campaignSplitValue(form.audienceText),
    sellingPoints: campaignSplitValue(form.sellingPointsText),
    contentBoundaries: campaignSplitValue(form.contentBoundariesText),
    riskBoundaries: campaignSplitValue(form.riskBoundariesText),
    assetCampaignLabels: campaignSplitValue(form.assetCampaignLabelsText || form.name),
  });

  const saveCampaign = async () => {
    if (!form) return;
    setSaving('saving');
    const response = await fetch(campaignApiUrl(`/api/initiatives/${form.id}`), {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ initiative: formPayload() }),
    });
    const data = await response.json();
    setCampaigns(rows => rows.map(item => item.id === data.initiative.id ? data.initiative : item));
    setSaving('saved');
    setTimeout(() => setSaving(''), 1200);
  };

  const createCampaign = async (type = 'standalone') => {
    const meta = INITIATIVE_TYPE_META[type] || INITIATIVE_TYPE_META.standalone;
    const response = await fetch(campaignApiUrl('/api/initiatives'), {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        type,
        name: `新 ${meta.label} ${campaigns.length + 1}`,
        brandId,
        status: 'draft',
        stage: meta.text,
        goals: ['沉淀社媒资产', '形成可复盘的内容证据'],
        platformFocus: ['小红书', '视频号', '微信公众号'],
      }),
    });
    const data = await response.json();
    setCampaigns(rows => [data.initiative, ...rows]);
    setActiveId(data.initiative.id);
  };

  const generateMasterBrief = async () => {
    if (!form) return;
    setSaving('brief');
    const response = await fetch(campaignApiUrl(`/api/initiatives/${form.id}/master-brief`), {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ initiative: formPayload() }),
    });
    const data = await response.json();
    setCampaigns(rows => rows.map(item => item.id === data.initiative.id ? data.initiative : item));
    setForm(prev => ({ ...prev, masterBrief: data.masterBrief }));
    setSaving('brief-saved');
    setTimeout(() => setSaving(''), 1200);
  };

  const writeCampaignToMemory = () => {
    if (!brandStoreReady || !brand || !activeCampaign) {
      setWriteStatus('品牌记忆未就绪');
      return;
    }
    const stampedAt = new Date().toLocaleString('zh-CN', { hour12: false });
    const memoryCampaign = {
      id: `initiative-${activeCampaign.id}`,
      name: activeCampaign.name,
      title: activeCampaign.name,
      status: activeCampaign.status,
      type: activeCampaign.type || 'campaign',
      source: 'campaign-workbench',
      factStatus: 'confirmed',
      provenance: 'operational',
      description: activeCampaign.coreMessage || activeCampaign.masterBrief?.coreMessage || '策略对象主命题待补充',
      metrics: { posts: metrics.posts, views: metrics.views, interaction: metrics.interaction, comments: metrics.comments },
      updatedAt: stampedAt,
    };
    const learning = {
      id: `initiative-learning-${activeCampaign.id}`,
      status: 'pending',
      type: 'content-initiative-social-ops',
      source: 'campaign-workbench',
      title: `${activeCampaign.name} 社媒运营闭环`,
      text: `已关联 ${metrics.posts} 条社媒内容，阅读/播放 ${campaignCompact(metrics.views)}，互动 ${campaignCompact(metrics.interaction)}，评论 ${campaignCompact(metrics.comments)}。`,
      createdAt: stampedAt,
    };
    brandState.setBrands(prev => prev.map(item => item.id === brand.id
      ? window.BrandMemoryStore.normalizeBrandProfile({
          ...item,
          campaigns: [...(item.campaigns || []).filter(row => row.id !== memoryCampaign.id), memoryCampaign],
          learnings: [...(item.learnings || []).filter(row => row.id !== learning.id), learning],
          updatedAt: stampedAt,
        })
      : item
    ));
    setWriteStatus('已写回品牌记忆');
  };

  if (!form) {
    return <div className="page wide"><div className="card"><div className="card-body">策略对象数据加载中…</div></div></div>;
  }

  return (
    <div className="page wide">
      <div className="page-header">
        <div className="heading">
          <h1>Social Strategy <span className="hl">工作台</span> · {form.name}</h1>
          <p>阶段性 Campaign、栏目、内容支柱和单篇内容都可以生成 Master Brief，并关联社媒资产复盘。</p>
        </div>
        <div className="actions">
          <button className="btn" onClick={() => createCampaign('standalone')}><Icon name="plus" className="icon sm"/>新内容</button>
          <button className="btn" onClick={() => createCampaign('series')}><Icon name="plus" className="icon sm"/>新栏目</button>
          <button className="btn" onClick={() => createCampaign('pillar')}><Icon name="plus" className="icon sm"/>新支柱</button>
          <button className="btn" onClick={() => createCampaign('campaign')}><Icon name="plus" className="icon sm"/>新 Campaign</button>
          <button className="btn" onClick={generateMasterBrief}><Icon name="spark" className="icon sm"/>生成 Master Brief</button>
          <button className="btn primary" onClick={saveCampaign}>{saving === 'saving' ? '保存中…' : saving === 'saved' ? '已保存' : '保存策略对象'}</button>
        </div>
      </div>

      <SocialStrategyModel activeType={form.type || 'campaign'} counts={campaigns.reduce((acc, item) => ({ ...acc, [item.type || 'campaign']: (acc[item.type || 'campaign'] || 0) + 1 }), {})}/>
      <StrategyThesisCard
        thesis={strategyThesis}
        initiatives={campaigns}
        status={thesisStatus}
        onChange={patchStrategyThesis}
        onSave={saveStrategyThesis}
        onApply={applyThesisToCurrentObject}
      />

      <div className="grid" style={{gridTemplateColumns:'260px 1fr 360px'}}>
        <div className="card">
          <div className="card-header"><h3>运营对象</h3><span className="count">{campaigns.length}</span></div>
          <div className="card-body flush">
            {campaigns.map(item => (
              <div key={item.id} onClick={() => setActiveId(item.id)} style={{padding:'12px 14px', cursor:'pointer', borderLeft: activeId === item.id ? '2px solid var(--accent)' : '2px solid transparent', background: activeId === item.id ? 'var(--accent-tint)' : 'transparent'}}>
                <div style={{display:'flex', gap:6, alignItems:'center'}}>
                  <span className={`tag mono ${INITIATIVE_TYPE_META[item.type || 'campaign']?.tone || 'ghost'}`}>{INITIATIVE_TYPE_META[item.type || 'campaign']?.label}</span>
                  <div style={{fontSize:13, fontWeight:600, minWidth:0, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{item.name}</div>
                </div>
                <div className="mono" style={{fontSize:10, color:'var(--ink-4)', marginTop:5}}>{item.status} · {item.stage}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="col">
          <div className="card">
            <div className="card-header"><h3>基础信息</h3><span className="count">{form.status}</span></div>
            <div className="card-body" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:10}}>
              <label style={{display:'flex', flexDirection:'column', gap:5}}>
                <span className="mono" style={{fontSize:10, color:'var(--ink-4)', textTransform:'uppercase'}}>类型</span>
                <select className="input" value={form.type || 'campaign'} onChange={e => patchForm('type', e.target.value)}>
                  {Object.entries(INITIATIVE_TYPE_META).map(([key, meta]) => <option key={key} value={key}>{meta.label} · {meta.text}</option>)}
                </select>
              </label>
              <CampaignField label="名称" value={form.name} onChange={value => patchForm('name', value)}/>
              <CampaignField label="阶段" value={form.stage} onChange={value => patchForm('stage', value)}/>
              <CampaignField label="开始日期" value={form.startDate} onChange={value => patchForm('startDate', value)}/>
              <CampaignField label="结束日期" value={form.endDate} onChange={value => patchForm('endDate', value)}/>
              <CampaignField label="内容支柱" value={form.pillarName} onChange={value => patchForm('pillarName', value)}/>
              <CampaignField label="栏目 / Series" value={form.seriesName} onChange={value => patchForm('seriesName', value)}/>
              <CampaignField wide label="本轮目标" value={form.goalsText} onChange={value => patchForm('goalsText', value)}/>
              <CampaignField wide label="重点平台" value={form.platformFocusText} onChange={value => patchForm('platformFocusText', value)}/>
              <CampaignField wide label="目标人群" value={form.audienceText} onChange={value => patchForm('audienceText', value)}/>
              <CampaignField wide label="核心命题" value={form.coreMessage} onChange={value => patchForm('coreMessage', value)}/>
              <CampaignField wide label="卖点优先级" value={form.sellingPointsText} onChange={value => patchForm('sellingPointsText', value)}/>
              <CampaignField wide label="内容边界" value={form.contentBoundariesText} onChange={value => patchForm('contentBoundariesText', value)}/>
              <CampaignField wide label="风险边界" value={form.riskBoundariesText} onChange={value => patchForm('riskBoundariesText', value)}/>
              <CampaignField wide label="资产匹配标签" value={form.assetCampaignLabelsText} onChange={value => patchForm('assetCampaignLabelsText', value)} helper="仅在没有明确挂接内容或 briefMapping 时兜底匹配；优先使用 assetPostIds 与 primaryMasterBriefId。"/>
            </div>
          </div>

          <CampaignMasterBrief brief={form.masterBrief} onGenerate={generateMasterBrief}/>
        </div>

        <div className="col">
          <MarketStrategyInputCard handoffs={marketHandoffs} onRefresh={loadMarketHandoffs} onApply={applyMarketHandoff}/>

          <div className="card">
            <div className="card-header"><h3>社媒资产闭环</h3><span className="count">{social.status}</span></div>
            <div className="card-body" style={{display:'grid', gap:10}}>
              <div className="grid g-2">
                <MiniMetric label="关联内容" value={metrics.posts}/>
                <MiniMetric label="阅读/播放" value={campaignCompact(metrics.views)}/>
                <MiniMetric label="互动" value={campaignCompact(metrics.interaction)}/>
                <MiniMetric label="评论" value={campaignCompact(metrics.comments)}/>
              </div>
              <button className="btn" onClick={loadSocial}><Icon name="refresh" className="icon sm"/>刷新社媒数据</button>
              <button className="btn primary" onClick={writeCampaignToMemory}><Icon name="memory" className="icon sm"/>写回品牌记忆</button>
              {writeStatus && <div className="callout" style={{margin:0, fontSize:12}}>{writeStatus}</div>}
            </div>
          </div>

          <div className="card">
            <div className="card-header"><h3>高表现内容</h3><span className="count">{topPosts.length}</span></div>
            <div className="card-body" style={{display:'grid', gap:8}}>
              {topPosts.length ? topPosts.map(post => (
                <div key={post.post_id} style={{padding:8, border:'1px solid var(--hairline)', borderRadius:6, background:'var(--bg-2)'}}>
                  <div style={{fontSize:12, fontWeight:600, lineHeight:1.4}}>{post.title || '未命名内容'}</div>
                  <small>{post.platform} · {post.product || '-'} · {post.campaign || '-'}</small>
                  <div className="mono" style={{fontSize:10, color:'var(--ink-4)', marginTop:4}}>阅读/播放 {campaignCompact(campaignViews(post))} · 互动 {campaignCompact(campaignInteraction(post))}</div>
                </div>
              )) : <div style={{fontSize:12, color:'var(--ink-4)'}}>还没有匹配到社媒内容，检查“资产匹配标签”或先同步社媒资产。</div>}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

const CampaignField = ({ label, value, onChange, wide, helper }) => (
  <label style={{display:'flex', flexDirection:'column', gap:5, gridColumn: wide ? '1 / -1' : 'auto'}}>
    <span className="mono" style={{fontSize:10, color:'var(--ink-4)', textTransform:'uppercase'}}>{label}</span>
    <textarea className="input" rows={wide ? 2 : 1} value={value || ''} onChange={e => onChange(e.target.value)} style={{minHeight: wide ? 58 : 36, resize:'vertical'}}/>
    {helper && <small style={{color:'var(--ink-4)'}}>{helper}</small>}
  </label>
);

const SocialStrategyModel = ({ activeType, counts = {} }) => (
  <div className="card">
    <div className="card-header">
      <h3>社媒运营内容策略模型</h3>
      <span className="count">Brief 可绑定任一策略对象 · attach to any object</span>
    </div>
    <div className="card-body" style={{display:'grid', gridTemplateColumns:'repeat(5, minmax(0, 1fr))', gap:8}}>
      {SOCIAL_STRATEGY_OBJECTS.map(item => (
        <div key={item.type} style={{padding:10, border:'1px solid var(--hairline)', borderColor: activeType === item.type ? 'var(--accent-line)' : 'var(--hairline)', background: activeType === item.type ? 'var(--accent-tint)' : 'var(--bg-2)', borderRadius:7, minHeight:132}}>
          <div style={{display:'flex', justifyContent:'space-between', gap:8}}>
            <div>
              <b style={{fontSize:12}}>{item.title}</b>
              <small style={{display:'block', marginTop:2, color:'var(--ink-4)', fontFamily:'var(--f-mono)', fontSize:10}}>{item.cn}</small>
            </div>
            <span className={`tag mono ${INITIATIVE_TYPE_META[item.type]?.tone || ''}`}>{counts[item.type] || 0}</span>
          </div>
          <div style={{display:'flex', gap:4, flexWrap:'wrap', marginTop:9}}>
            {item.examples.map(example => (
              <span key={`${example.en}-${example.cn}`} className="tag mono" style={{display:'inline-flex', flexDirection:'column', alignItems:'flex-start', gap:2, lineHeight:1.15}}>
                <span>{example.en}</span>
                <span style={{fontFamily:'var(--f-sans)', color:'var(--ink-3)'}}>{example.cn}</span>
              </span>
            ))}
          </div>
        </div>
      ))}
    </div>
  </div>
);

const StrategyThesisCard = ({ thesis = DEFAULT_STRATEGY_THESIS, initiatives = [], status, onChange, onSave, onApply }) => {
  const simulation = buildStrategySimulation({
    ...DEFAULT_STRATEGY_THESIS,
    ...thesis,
    contentParadigm: strategySplitList(thesis.contentParadigmText || strategyText(thesis.contentParadigm)),
  }, initiatives);
  const covered = simulation.filter(item => item.status === 'covered').length;
  return (
    <div className="card">
      <div className="card-header">
        <h3>Strategy Thesis · 策略母题</h3>
        <span className="count">{thesis.timeframe || '2026-2027'} · {covered}/{simulation.length} covered</span>
        <div className="actions">
          <button className="btn sm ghost" onClick={onApply}>应用到当前对象</button>
          <button className="btn sm primary" onClick={onSave}>{status === 'saving' ? '保存中…' : status === 'saved' ? '已保存' : status === 'applied' ? '已应用' : '保存母题'}</button>
        </div>
      </div>
      <div className="card-body" style={{display:'grid', gridTemplateColumns:'1.05fr 1.2fr', gap:12}}>
        <div style={{display:'grid', gap:10}}>
          <CampaignField label="标题 / Title" value={thesis.title} onChange={value => onChange('title', value)}/>
          <CampaignField label="周期 / Timeframe" value={thesis.timeframe} onChange={value => onChange('timeframe', value)}/>
          <CampaignField wide label="核心判断 / Core Thesis" value={thesis.coreThesis} onChange={value => onChange('coreThesis', value)}/>
          <CampaignField wide label="市场信念 / Market Belief" value={thesis.marketBelief} onChange={value => onChange('marketBelief', value)}/>
          <CampaignField wide label="内容范式 / Content Paradigm" value={thesis.contentParadigmText || strategyText(thesis.contentParadigm)} onChange={value => onChange('contentParadigmText', value)} helper="用顿号、逗号或换行分隔。"/>
          <CampaignField wide label="护栏 / Guardrails" value={thesis.guardrailsText || strategyText(thesis.guardrails)} onChange={value => onChange('guardrailsText', value)}/>
        </div>
        <div style={{display:'grid', gap:8}}>
          <div style={{padding:12, border:'1px solid var(--hairline)', borderRadius:7, background:'var(--bg-2)'}}>
            <div className="mono" style={{fontSize:10, color:'var(--ink-4)', textTransform:'uppercase', marginBottom:6}}>Content Strategy Simulation</div>
            <div style={{fontSize:15, lineHeight:1.65, fontFamily:'var(--f-serif)'}}>{thesis.coreThesis || DEFAULT_STRATEGY_THESIS.coreThesis}</div>
            <p style={{margin:'6px 0 0', fontSize:12, color:'var(--ink-3)', lineHeight:1.6}}>{thesis.marketBelief || DEFAULT_STRATEGY_THESIS.marketBelief}</p>
          </div>
          <div style={{display:'grid', gap:6, maxHeight:468, overflow:'auto', paddingRight:2}}>
            {simulation.map((item, index) => (
              <div key={`${item.stage}-${index}`} style={{padding:9, border:'1px solid var(--hairline)', borderColor:item.status === 'covered' ? 'var(--accent-line)' : 'var(--hairline)', borderRadius:7, background:item.status === 'covered' ? 'var(--accent-tint)' : 'var(--bg-2)'}}>
                <div style={{display:'flex', justifyContent:'space-between', gap:8, alignItems:'center'}}>
                  <b style={{fontSize:12}}>{index + 1}. {item.stage}</b>
                  <span className={`tag mono ${item.status === 'covered' ? 'accent' : 'ghost'}`}>{item.status === 'covered' ? '已有承接' : '内容缺口'}</span>
                </div>
                <small style={{display:'block', marginTop:4, color:'var(--ink-3)', lineHeight:1.45}}>{item.hint}</small>
                {item.objects.length ? (
                  <div style={{display:'flex', gap:4, flexWrap:'wrap', marginTop:7}}>
                    {item.objects.map(object => <span key={object.id} className="tag mono">{INITIATIVE_TYPE_META[object.type || 'standalone']?.label || 'Object'} · {object.name}</span>)}
                  </div>
                ) : null}
                <div className="mono" style={{fontSize:10, color:'var(--ink-4)', marginTop:7}}>{item.recommendation}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

const CampaignMasterBrief = ({ brief, onGenerate }) => (
  <div className="card">
    <div className="card-header">
      <h3>Master Brief</h3>
      <span className="count">{brief?.version || 'v1'}</span>
      <div className="actions"><button className="btn sm ghost" onClick={onGenerate}>重新生成</button></div>
    </div>
    <div className="card-body" style={{display:'grid', gap:12}}>
      <BriefSection label="策略对象背景" value={brief?.background}/>
      <BriefSection label="本轮目标" value={brief?.objective}/>
      <BriefSection label="品牌摘要" value={brief?.brandSummary}/>
      <BriefSection label="目标人群摘要" value={brief?.audienceSummary}/>
      <BriefSection label="核心命题" value={brief?.coreMessage} emphasis/>
      <BriefSection label="卖点优先级" value={brief?.sellingPoints}/>
      <BriefSection label="内容边界" value={brief?.contentBoundaries}/>
      <BriefSection label="风险边界" value={brief?.riskBoundaries}/>
      <BriefSection label="平台约束摘要" value={brief?.platformRequirements}/>
      <BriefSection label="CTA 要求" value={brief?.cta}/>
      <BriefSection label="QA Checklist" value={brief?.qaChecklist}/>
    </div>
  </div>
);

const BriefSection = ({ label, value, emphasis }) => {
  const items = Array.isArray(value) ? value : [];
  return (
    <div>
      <div className="mono" style={{fontSize:10, color:'var(--ink-4)', textTransform:'uppercase', marginBottom:5}}>{label}</div>
      {items.length ? (
        <div style={{display:'flex', gap:5, flexWrap:'wrap'}}>{items.map(item => <span key={item} className="tag">{item}</span>)}</div>
      ) : (
        <div style={{fontSize: emphasis ? 16 : 13, fontFamily: emphasis ? 'var(--f-serif)' : 'inherit', lineHeight:1.65, color:'var(--ink-2)'}}>{value || '-'}</div>
      )}
    </div>
  );
};

const MiniMetric = ({ label, value }) => (
  <div style={{padding:10, background:'var(--bg-2)', border:'1px solid var(--hairline)', borderRadius:6}}>
    <div className="mono" style={{fontSize:9, color:'var(--ink-4)', textTransform:'uppercase'}}>{label}</div>
    <div style={{fontSize:18, fontFamily:'var(--f-serif)', marginTop:2}}>{value}</div>
  </div>
);

const MarketStrategyInputCard = ({ handoffs = [], onRefresh, onApply }) => {
  const active = handoffs[0] || null;
  const pack = active?.contextPack || {};
  const snapshot = pack.market_snapshot || {};
  const riskNotes = pack.risk_notes || [];
  const signals = snapshot.active_signals || [];
  return (
    <div className="card strategy-market-input">
      <div className="card-header">
        <h3>Market Context 输入</h3>
        <span className="count">{handoffs.length}</span>
        <div className="actions"><button className="btn sm ghost" onClick={onRefresh}>刷新</button></div>
      </div>
      <div className="card-body" style={{display:'grid', gap:10}}>
        {active ? (
          <>
            <p>{snapshot.summary || 'Market Context Pack 已就绪。'}</p>
            <div className="grid g-3">
              <MiniMetric label="signals" value={signals.length}/>
              <MiniMetric label="risks" value={riskNotes.length}/>
              <MiniMetric label="confidence" value={pack.confidence ? `${Math.round(pack.confidence * 100)}%` : '-'}/>
            </div>
            <div className="market-signal-themes">
              {(pack.experience_patterns || []).slice(0, 2).map(item => <span key={item.pattern_id || item.title} className="tag mono">{item.title}</span>)}
              {(pack.competitive_benchmark?.white_spaces || []).slice(0, 2).map(item => <span key={item} className="tag mono">{item}</span>)}
            </div>
            <button className="btn primary" onClick={() => onApply(active)}><Icon name="arrow" className="icon sm"/>应用到策略对象</button>
          </>
        ) : (
          <div className="market-empty-state">还没有来自市场感知的 Strategy handoff。先在市场感知页点击“交给 Strategy”。</div>
        )}
      </div>
    </div>
  );
};

// ========== 04 · CREATORS ==========
const Creators = () => {
  const [picked, setPicked] = React.useState(() => CREATORS.filter(c => c.picked).map(c => c.handle));
  const [tierFilter, setTierFilter] = React.useState('全部');

  const toggle = (h) => setPicked(p => p.includes(h) ? p.filter(x => x !== h) : [...p, h]);

  const filtered = tierFilter === '全部' ? CREATORS : CREATORS.filter(c => c.tier === tierFilter);
  const pickedObjs = CREATORS.filter(c => picked.includes(c.handle));
  const totalBudget = pickedObjs.reduce((s, c) => s + c.price, 0);

  return (
    <div className="page wide">
      <div className="page-header">
        <div className="heading">
          <h1>达人匹配 · <span className="hl">分层组合</span>建议</h1>
          <p>不只按粉丝和报价排序。调性 · 人群重合 · 评论质量 · 稳定性 · 风险，综合匹配本轮命题。</p>
        </div>
        <div className="actions">
          <button className="btn"><Icon name="filter" className="icon sm"/>高级筛选</button>
          <button className="btn primary">提交组合 <Icon name="arrow" className="icon sm"/></button>
        </div>
      </div>

      <div className="grid g-4">
        <MetricCard label="候选池" value="412" delta="从 3,480 过滤"/>
        <MetricCard label="已选达人" value={String(picked.length)} delta={`${pickedObjs.length}/${CREATORS.length} 推荐`} accent/>
        <MetricCard label="预计预算" value={`¥${totalBudget}w`} delta="限额 ¥486w"/>
        <MetricCard label="组合适配度" value="88" delta="加权 fit score"/>
      </div>

      <div className="grid" style={{gridTemplateColumns:'1fr 340px'}}>
        <div className="card">
          <div className="card-header">
            <h3>候选达人 · 按 fit 排序</h3>
            <span className="count">{filtered.length}</span>
            <div className="actions" style={{gap:4}}>
              {['全部','策略型','放大型','验证型'].map(t => (
                <button key={t} className={`btn sm ${tierFilter === t ? 'primary' : 'ghost'}`} onClick={() => setTierFilter(t)}>{t}</button>
              ))}
            </div>
          </div>
          <div className="card-body flush">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:40}}></th>
                  <th>达人</th>
                  <th>平台</th>
                  <th>分层</th>
                  <th>粉丝</th>
                  <th>Fit</th>
                  <th>情绪</th>
                  <th>风险</th>
                  <th style={{textAlign:'right'}}>报价 (w)</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(c => (
                  <tr key={c.handle} style={picked.includes(c.handle) ? {background:'var(--accent-tint)'} : {}}>
                    <td>
                      <input type="checkbox" checked={picked.includes(c.handle)} onChange={() => toggle(c.handle)} style={{cursor:'pointer', accentColor:'var(--accent)'}}/>
                    </td>
                    <td>
                      <div style={{display:'flex', alignItems:'center', gap:8}}>
                        <div className="avatar" style={{width:22, height:22, fontSize:10}}>{c.avatar}</div>
                        <div>
                          <div style={{fontWeight:500}}>{c.handle}</div>
                          <div style={{fontSize:10, color:'var(--ink-4)'}}>{c.tags.join(' · ')}</div>
                        </div>
                      </div>
                    </td>
                    <td><span className="tag" style={{fontSize:10}}>{c.platform}</span></td>
                    <td><TierBadge tier={c.tier}/></td>
                    <td className="mono" style={{fontSize:11}}>{c.followers}</td>
                    <td>
                      <div style={{display:'flex', alignItems:'center', gap:6}}>
                        <div className="bar accent" style={{width:40}}><span style={{width: c.fit + '%'}}></span></div>
                        <span className="mono" style={{fontSize:11}}>{c.fit}</span>
                      </div>
                    </td>
                    <td>
                      <span style={{color: c.sentiment > 0.5 ? 'var(--accent)' : c.sentiment > 0.4 ? 'var(--ink-2)' : 'var(--warn)', fontFamily:'var(--f-mono)', fontSize:11}}>
                        +{c.sentiment.toFixed(2)}
                      </span>
                    </td>
                    <td>
                      <span className={`chip ${c.risk === 'low' ? '' : c.risk === 'med' ? 'warn' : 'danger'}`} style={{fontSize:10}}>
                        <span className="dot"></span>{{low:'低',med:'中',high:'高'}[c.risk]}
                      </span>
                    </td>
                    <td style={{textAlign:'right', fontFamily:'var(--f-mono)'}}>{c.price}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        <div className="col">
          <div className="card">
            <div className="card-header"><h3>组合分层 · 已选</h3></div>
            <div className="card-body flush">
              {['策略型', '放大型', '验证型'].map(tier => {
                const list = pickedObjs.filter(c => c.tier === tier);
                return (
                  <div key={tier} style={{padding:12, borderBottom:'1px solid var(--hairline)'}}>
                    <div style={{display:'flex', alignItems:'baseline', justifyContent:'space-between', marginBottom:8}}>
                      <TierBadge tier={tier}/>
                      <span className="mono" style={{fontSize:10, color:'var(--ink-4)'}}>{list.length} 位</span>
                    </div>
                    <div style={{fontSize:10, color:'var(--ink-4)', marginBottom:8, fontFamily:'var(--f-mono)'}}>
                      {{'策略型':'定义命题 · 建立认知','放大型':'扩散命题 · 稳定放量','验证型':'低成本测试人群'}[tier]}
                    </div>
                    <div style={{display:'flex', flexDirection:'column', gap:4}}>
                      {list.length === 0 && <div style={{fontSize:11, color:'var(--ink-4)', fontStyle:'italic'}}>未选入</div>}
                      {list.map(c => (
                        <div key={c.handle} style={{display:'flex', alignItems:'center', gap:6, fontSize:11}}>
                          <div className="avatar" style={{width:18, height:18, fontSize:9}}>{c.avatar}</div>
                          <span style={{flex:1}}>{c.handle}</span>
                          <span className="mono" style={{color:'var(--ink-4)'}}>¥{c.price}w</span>
                        </div>
                      ))}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          <div className="card">
            <div className="card-header"><h3>组合诊断</h3></div>
            <div className="card-body" style={{display:'flex', flexDirection:'column', gap:10}}>
              <DiagLine label="人群覆盖" value="家庭 · 设计师 · 驾驶爱好者" ok/>
              <DiagLine label="平台平衡" value="小红书偏多 · 抖音可再加 1" warn/>
              <DiagLine label="风险状况" value="已排除 1 位高危达人" ok/>
              <DiagLine label="预算利用" value={`¥${totalBudget}w / ¥486w`} ok/>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

const TierBadge = ({ tier }) => {
  const map = { '策略型': ['var(--accent)', 'var(--accent-tint)', 'var(--accent-line)'], '放大型': ['var(--info)', 'var(--info-tint)', 'color-mix(in oklch, var(--info) 30%, var(--hairline))'], '验证型': ['var(--ink-2)', 'var(--bg-2)', 'var(--hairline)'] };
  const [c, bg, bd] = map[tier] || map['验证型'];
  return <span className="tag" style={{color:c, background:bg, borderColor:bd, fontSize:10, fontFamily:'var(--f-mono)'}}>{tier}</span>;
};

const DiagLine = ({ label, value, ok, warn }) => (
  <div style={{display:'flex', alignItems:'flex-start', gap:10}}>
    <span style={{width:6, height:6, borderRadius:'50%', background: ok ? 'var(--accent)' : 'var(--warn)', marginTop:6, flexShrink:0}}></span>
    <div style={{flex:1}}>
      <div style={{fontFamily:'var(--f-mono)', fontSize:10, color:'var(--ink-4)', textTransform:'uppercase'}}>{label}</div>
      <div style={{fontSize:12}}>{value}</div>
    </div>
  </div>
);

Object.assign(window, { Strategy, Creators });
