// PLAN-02 · 경쟁사·뉴스 일일 다이제스트
// 난이도 ★★ · 예상 45분 · 전제: 구글 계정 + Gemini API 키 + (선택) Slack
// 출처: https://www.nedabah.org/resources/automation/planning/02-competitor-news-digest.html
// Apps Script 편집기에 붙여넣은 뒤 스크립트 속성에 PROPS 항목을 등록하세요.

const PROPS = PropertiesService.getScriptProperties();
const GEMINI_KEY = PROPS.getProperty('GEMINI_API_KEY');
const SHEET_ID   = PROPS.getProperty('SHEET_ID');
const EMAIL_TO   = PROPS.getProperty('RECIPIENT_EMAIL') || '';
const SLACK_HOOK = PROPS.getProperty('SLACK_WEBHOOK') || '';

const LOOKBACK_HOURS = 24;
const MAX_PER_KEYWORD = 8;

function dailyDigest() {
  const ss = SpreadsheetApp.openById(SHEET_ID);
  const kw = ss.getSheetByName('keywords').getDataRange().getValues();
  const log = ss.getSheetByName('digest_log');

  const since = new Date(Date.now() - LOOKBACK_HOURS * 3600 * 1000);
  const collected = [];

  for (let i = 1; i < kw.length; i++) {
    const [category, keyword, rssUrl] = kw[i];
    if (!rssUrl) continue;
    const items = fetchRSS(rssUrl, since).slice(0, MAX_PER_KEYWORD);
    items.forEach(it => collected.push({...it, category, keyword}));
    Utilities.sleep(800);
  }

  if (collected.length === 0) {
    Logger.log('수집된 뉴스 0건. 다이제스트 생략.');
    return;
  }

  const dedup = deduplicate(collected);
  // Gemini 입력 토큰 한도를 넘지 않도록 50건 단위로 청크 처리
  const summarized = [];
  for (let i = 0; i < dedup.length; i += 50) {
    const chunk = dedup.slice(i, i + 50);
    summarized.push(...summarizeBatch(chunk));
    if (i + 50 < dedup.length) Utilities.sleep(2000);
  }
  const html = buildDigestHTML(summarized);

  // 로그 적재
  const today = new Date().toISOString().slice(0,10);
  summarized.forEach(s => log.appendRow([today, s.title, s.url, s.category, s.summary]));

  if (EMAIL_TO) {
    GmailApp.sendEmail(EMAIL_TO, `[데일리] ${today} 경쟁사·산업 다이제스트`, '', {htmlBody: html});
  }
  if (SLACK_HOOK) {
    UrlFetchApp.fetch(SLACK_HOOK, {
      method: 'post', contentType: 'application/json',
      payload: JSON.stringify({text: htmlToSlackText(summarized, today)})
    });
  }
}

function fetchRSS(url, since) {
  try {
    const xml = UrlFetchApp.fetch(url, {muteHttpExceptions: true}).getContentText();
    const doc = XmlService.parse(xml);
    const items = doc.getRootElement().getChild('channel').getChildren('item');
    return items.map(it => ({
      title: it.getChildText('title') || '',
      url:   it.getChildText('link')  || '',
      pub:   new Date(it.getChildText('pubDate') || Date.now()),
      desc:  (it.getChildText('description') || '').replace(/<[^>]+>/g, '').slice(0, 500)
    })).filter(it => it.pub >= since);
  } catch(e) {
    Logger.log(`RSS 실패 ${url}: ${e}`);
    return [];
  }
}

function deduplicate(items) {
  const seen = new Set();
  return items.filter(it => {
    const key = it.title.replace(/\s+/g, '').slice(0, 30);
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function summarizeBatch(items) {
  const prompt = `
다음 뉴스 ${items.length}건을 각각 한국어 1~2문장으로 요약하세요.
- 핵심 사실만, 추측 금지
- "~다"체로 통일
- JSON 배열만 반환: [{"summary":"..."},...]
순서 유지.

뉴스:
${items.map((it,i)=>`[${i+1}] ${it.title}\n${it.desc}`).join('\n\n')}
`;
  const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_KEY}`;
  const res = UrlFetchApp.fetch(url, {
    method: 'post', contentType: 'application/json',
    payload: JSON.stringify({
      contents:[{parts:[{text: prompt}]}],
      generationConfig:{responseMimeType: 'application/json'}
    })
  });
  const arr = JSON.parse(JSON.parse(res.getContentText()).candidates[0].content.parts[0].text);
  return items.map((it, i) => ({...it, summary: (arr[i]||{}).summary || ''}));
}

function buildDigestHTML(items) {
  const grouped = {};
  items.forEach(it => {
    grouped[it.category] = grouped[it.category] || [];
    grouped[it.category].push(it);
  });
  let html = `<div style="font-family:'Noto Sans KR',sans-serif;max-width:680px;line-height:1.6;">
    <h2 style="border-bottom:2px solid #b45309;padding-bottom:.4rem;">📊 데일리 다이제스트</h2>
    <p style="color:#6a604f;">${new Date().toLocaleDateString('ko-KR')} · 총 ${items.length}건</p>`;
  for (const cat in grouped) {
    html += `<h3 style="margin-top:1.5rem;color:#3a322a;">${cat}</h3><ul>`;
    grouped[cat].forEach(it => {
      html += `<li style="margin-bottom:.6rem;"><a href="${it.url}" style="color:#b45309;text-decoration:none;font-weight:600;">${it.title}</a><br><span style="color:#555;font-size:.95em;">${it.summary}</span></li>`;
    });
    html += `</ul>`;
  }
  html += `<p style="color:#999;font-size:.85em;margin-top:2rem;">자동 생성 · 출처는 각 링크 참조</p></div>`;
  return html;
}

function htmlToSlackText(items, date) {
  const grouped = {};
  items.forEach(it => { grouped[it.category] = grouped[it.category] || []; grouped[it.category].push(it); });
  let txt = `*📊 ${date} 데일리 다이제스트* (${items.length}건)\n`;
  for (const cat in grouped) {
    txt += `\n*${cat}*\n`;
    grouped[cat].forEach(it => txt += `• <${it.url}|${it.title}> — ${it.summary}\n`);
  }
  return txt;
}