// MKT-03 · 리뷰·멘션 주간 다이제스트
// 난이도 ★★ · 예상 45분 · 전제: 네이버 검색 API 키 + Gemini API 키
// 출처: https://www.nedabah.org/resources/automation/marketing/03-review-mention-digest.html
// Apps Script 편집기에 붙여넣은 뒤 스크립트 속성에 PROPS 항목을 등록하세요.

const PROPS = PropertiesService.getScriptProperties();
const SHEET_ID = PROPS.getProperty('SHEET_ID');
const N_ID = PROPS.getProperty('NAVER_ID');
const N_SEC = PROPS.getProperty('NAVER_SECRET');
const GEMINI = PROPS.getProperty('GEMINI_API_KEY');
const EMAIL_TO = PROPS.getProperty('RECIPIENT_EMAIL');
const URGENT = PROPS.getProperty('URGENT_SLACK_HOOK') || '';

// ─────────────────────────────────────────────
// 1) 매일 수집
// ─────────────────────────────────────────────
function dailyCollect() {
  const ss = SpreadsheetApp.openById(SHEET_ID);
  const kws = ss.getSheetByName('keywords').getDataRange().getValues().slice(1);
  const dst = ss.getSheetByName('mentions');
  const today = new Date().toISOString().slice(0,10);

  // 중복 방지
  const seen = new Set(dst.getRange('E2:E').getValues().flat().map(String));

  const newItems = [];
  kws.forEach(([keyword, kind]) => {
    if (!keyword) return;
    ['blog','cafearticle','news'].forEach(api => {
      const items = naverSearch(api, keyword);
      items.forEach(it => {
        if (seen.has(it.link)) return;
        seen.add(it.link);
        newItems.push({date: today, keyword, kind, source: api, title: stripHtml(it.title), url: it.link, snippet: stripHtml(it.description||'')});
      });
      Utilities.sleep(300);
    });
  });

  if (newItems.length === 0) return;

  // AI 감성·주제 분류 (배치)
  for (let i = 0; i < newItems.length; i += 30) {
    const slice = newItems.slice(i, i+30);
    const cls = classify(slice);
    slice.forEach((it, idx) => {
      const c = cls[idx] || {};
      dst.appendRow([it.date, it.keyword, it.source, it.title, it.url, it.snippet, c.sentiment || '중립', (c.topics||[]).join(',')]);
      // 부정 즉시 알림
      if (c.sentiment === 'negative' && URGENT) {
        UrlFetchApp.fetch(URGENT, {
          method:'post', contentType:'application/json',
          payload: JSON.stringify({text: `:warning: 부정 멘션 — *${it.keyword}*\n• <${it.url}|${it.title}>\n• 주제: ${(c.topics||[]).join(', ')}`})
        });
      }
    });
    Utilities.sleep(2000);
  }
}

function naverSearch(api, query) {
  const url = `https://openapi.naver.com/v1/search/${api}.json?query=${encodeURIComponent(query)}&display=20&sort=date`;
  try {
    const res = UrlFetchApp.fetch(url, {
      headers: {'X-Naver-Client-Id': N_ID, 'X-Naver-Client-Secret': N_SEC},
      muteHttpExceptions: true
    });
    if (res.getResponseCode() !== 200) return [];
    return JSON.parse(res.getContentText()).items || [];
  } catch(e) { return []; }
}

function stripHtml(s) { return String(s||'').replace(/<[^>]+>/g, '').replace(/&[a-z]+;/gi, ' '); }

function classify(items) {
  const prompt = `
각 멘션에 대해 한국어로 분류하세요. JSON 배열만 반환.
- sentiment: positive/negative/neutral
- topics: 1~3개 짧은 명사구 (가격/품질/배송/디자인/UX/CS/마케팅/이벤트/리더십/위기/기타)

멘션:
${items.map((it,i)=>`[${i+1}] ${it.title} — ${it.snippet.slice(0,300)}`).join('\n')}

[{"sentiment":"...","topics":["..."]}]
`;
  const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI}`;
  const res = UrlFetchApp.fetch(url, {method:'post', contentType:'application/json',
    payload: JSON.stringify({contents:[{parts:[{text:prompt}]}], generationConfig:{responseMimeType:'application/json'}})
  });
  return JSON.parse(JSON.parse(res.getContentText()).candidates[0].content.parts[0].text);
}

// ─────────────────────────────────────────────
// 2) 주간 리포트
// ─────────────────────────────────────────────
function weeklyReport() {
  const ss = SpreadsheetApp.openById(SHEET_ID);
  const all = ss.getSheetByName('mentions').getDataRange().getValues();
  const head = all[0];
  const since = new Date(Date.now() - 7*86400000);
  const week = all.slice(1).filter(r => new Date(r[0]) >= since);
  if (week.length === 0) return;

  const grouped = {};
  let pos=0, neg=0, neu=0;
  const topicCount = {};
  week.forEach(r => {
    const [date, keyword, source, title, url, snippet, sentiment, topics] = r;
    grouped[keyword] = grouped[keyword] || {pos:0, neg:0, neu:0, items:[]};
    grouped[keyword].items.push({title, url, sentiment, topics, snippet});
    if (sentiment === 'positive') {pos++; grouped[keyword].pos++;}
    else if (sentiment === 'negative') {neg++; grouped[keyword].neg++;}
    else {neu++; grouped[keyword].neu++;}
    String(topics||'').split(',').filter(Boolean).forEach(t => topicCount[t.trim()] = (topicCount[t.trim()]||0)+1);
  });

  const topTopics = Object.entries(topicCount).sort((a,b)=>b[1]-a[1]).slice(0, 8);

  const insight = aiInsight({total: week.length, pos, neg, neu, topTopics, grouped});
  const html = render(week.length, {pos,neg,neu}, topTopics, grouped, insight);

  if (EMAIL_TO) GmailApp.sendEmail(EMAIL_TO, `[주간] 브랜드 멘션 다이제스트 — ${new Date().toISOString().slice(0,10)}`, '', {htmlBody: html});
  ss.getSheetByName('weekly_report').appendRow([new Date(), week.length, pos, neg, neu, JSON.stringify(insight)]);
}

function aiInsight(d) {
  const ctx = d.topTopics.map(([t,c])=>`${t}(${c})`).join(', ');
  const prompt = `
브랜드 매니저용 주간 코멘트 작성. 한국어 250자 이내.
데이터:
- 총 멘션: ${d.total}
- 감성: 긍정 ${d.pos} / 부정 ${d.neg} / 중립 ${d.neu}
- 상위 주제: ${ctx}

JSON: {"summary":"...","watch":"...","action":"..."}
- summary: 한 문장 종합
- watch: 다음 주 주의해서 지켜볼 한 가지
- action: 마케팅·CS·제품팀 중 한 곳에 보낼 권고 한 줄
`;
  const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI}`;
  const r = UrlFetchApp.fetch(url, {method:'post', contentType:'application/json',
    payload: JSON.stringify({contents:[{parts:[{text:prompt}]}], generationConfig:{responseMimeType:'application/json'}})
  });
  return JSON.parse(JSON.parse(r.getContentText()).candidates[0].content.parts[0].text);
}

function render(total, sent, topTopics, grouped, ins) {
  const sumTotal = sent.pos + sent.neg + sent.neu || 1;
  const topicHtml = topTopics.map(([t,c])=>`<li><b>${t}</b> · ${c}건</li>`).join('');
  let kwHtml = '';
  for (const k in grouped) {
    const g = grouped[k];
    const negs = g.items.filter(it=>it.sentiment==='negative').slice(0,3);
    const negHtml = negs.length ? `<ul>${negs.map(n=>`<li><a href="${n.url}">${n.title}</a></li>`).join('')}</ul>` : '<p style="color:#888;">부정 멘션 없음</p>';
    kwHtml += `<h4>${k}</h4>
      <p>긍정 ${g.pos} / 부정 ${g.neg} / 중립 ${g.neu}</p>
      <p style="color:#b45309;"><b>주의 멘션</b></p>${negHtml}`;
  }
  return `<div style="font-family:'Noto Sans KR',sans-serif;line-height:1.7;max-width:740px;color:#222;">
    <h2 style="border-bottom:2px solid #b45309;padding-bottom:.5rem;">브랜드 멘션 주간 다이제스트</h2>
    <p style="color:#6a604f;">${new Date().toLocaleDateString('ko-KR')} · 총 ${total}건</p>
    <h3>전체 감성</h3>
    <p>긍정 ${(sent.pos/sumTotal*100).toFixed(0)}% / 부정 ${(sent.neg/sumTotal*100).toFixed(0)}% / 중립 ${(sent.neu/sumTotal*100).toFixed(0)}%</p>
    <h3>상위 주제</h3>
    <ul>${topicHtml}</ul>
    <h3>📌 핵심 인사이트</h3>
    <p><b>요약 — </b>${ins.summary}</p>
    <p><b>다음 주 관찰 — </b>${ins.watch}</p>
    <p><b>권고 액션 — </b>${ins.action}</p>
    <hr>
    <h3>키워드별 상세</h3>
    ${kwHtml}
    <p style="color:#999;font-size:.85em;margin-top:2rem;">자동 생성 · 출처: 네이버 블로그/카페/뉴스</p>
  </div>`;
}