/**
 * GmailToWebhook.js — Yahoo Message Manager / GAS初版（Pull方式 doGet）
 * --------------------------------------------------------------------------
 * 役割:
 *   ローカルの FastAPI backend からの GET リクエストを受け、
 *   Gmail内のヤフオク「取引メッセージ」通知メールを検索してJSONで返す。
 *
 * セキュリティ:
 *   - TOKEN は本ファイルに直書きしない。
 *     スクリプトエディタ「プロジェクトの設定 > スクリプトプロパティ」に
 *     WEBHOOK_TOKEN という名前で保存する。
 *   - 同じトークン値をローカル config.json の gas_token にも設定する。
 *
 * デプロイ:
 *   ウェブアプリとしてデプロイ / 実行ユーザー: 自分 / アクセス: 全員
 *
 * 確認済みメール仕様（2026-06-04 実メールより）:
 *   From   : auction-master@mail.yahoo.co.jp
 *   Subject: Yahoo!オークション - 取引メッセージ：{商品名}({オークションID})
 *   Body   : "落札者から取引メッセージが届きました。" + 取引ナビURL + オークションID
 *   ※ 相手のメッセージ本文はメールに含まれない（取引ナビで確認する形式）
 */

// ===== 設定 =====
var MAX_MESSAGES_PER_RUN = 50;
var DEFAULT_SINCE_DAYS   = 30;

// 取引メッセージ通知だけを拾うクエリ（汎用・ラベル非依存）。
// 既存の Gmail ラベルを使う場合は下のコメント例に差し替え可。
//   var GMAIL_QUERY = 'label:"ヤフオク/取引メッセージ"';
var GMAIL_QUERY = 'from:auction-master@mail.yahoo.co.jp subject:取引メッセージ';

// ===== エントリポイント =====
function doGet(e) {
  try {
    var params = (e && e.parameter) ? e.parameter : {};
    var configuredToken = getToken_();

    // トークン未設定 or 不一致は 403相当で拒否
    if (!configuredToken || params.token !== configuredToken) {
      return jsonOutput_({ ok: false, error: 'unauthorized', count: 0, messages: [] });
    }

    var query = buildQuery_(params.since);
    var threads = GmailApp.search(query, 0, MAX_MESSAGES_PER_RUN);

    var rawCount = 0;       // 件名再フィルタ前のメッセージ数
    var subjects = [];      // debug用（件名のみ。個人情報は含まない）
    var messages = [];
    for (var i = 0; i < threads.length && messages.length < MAX_MESSAGES_PER_RUN; i++) {
      var msgs = threads[i].getMessages();
      for (var j = 0; j < msgs.length && messages.length < MAX_MESSAGES_PER_RUN; j++) {
        var m = msgs[j];
        rawCount++;
        var subj = m.getSubject() || '';
        if (subjects.length < 10) subjects.push(subj.slice(0, 60));
        // スレッドに別種メールが混在する場合に備え件名で再フィルタ
        if (subj.indexOf('取引メッセージ') === -1) continue;
        messages.push(buildMessage_(m));
      }
    }

    // 診断モード: ?debug=1 → クエリ・件数・件名のみ返す（0件時の原因切り分け用）
    if (params.debug === '1') {
      return jsonOutput_({
        ok: true,
        debug: true,
        query: query,
        thread_count: threads.length,
        raw_message_count: rawCount,
        filtered_count: messages.length,
        subjects: subjects,
        fetched_at: nowIso_()
      });
    }

    return jsonOutput_({
      ok: true,
      count: messages.length,
      messages: messages,
      fetched_at: nowIso_()        // backend の since 追跡用（任意フィールド）
    });

  } catch (err) {
    return jsonOutput_({ ok: false, error: String(err), count: 0, messages: [] });
  }
}

// ===== 内部関数 =====

// トークンは WEBHOOK_TOKEN を優先。互換のため GAS_TOKEN もフォールバックで参照。
function getToken_() {
  var p = PropertiesService.getScriptProperties();
  return p.getProperty('WEBHOOK_TOKEN') || p.getProperty('GAS_TOKEN') || '';
}

// since（ISO日付文字列）があれば after:YYYY/MM/DD を付ける。なければ newer_than。
// after: は日付粒度のため当日分は重複し得るが、backend が source_message_id で重複排除する。
function buildQuery_(since) {
  var q = GMAIL_QUERY;
  var after = toGmailAfter_(since);
  if (after) {
    q += ' after:' + after;
  } else {
    q += ' newer_than:' + DEFAULT_SINCE_DAYS + 'd';
  }
  return q;
}

function buildMessage_(m) {
  var subject    = m.getSubject() || '';
  var sender     = m.getFrom() || '';
  var receivedIso = m.getDate().toISOString();   // UTC ISO（例: 2026-06-04T02:09:00.000Z）
  var body       = getBodyText_(m);              // プレーンテキスト優先・HTMLはフォールバック

  return {
    source_message_id:    m.getId(),             // Gmail message id（常に取得可能）
    received_at:          receivedIso,
    sender:               sender,
    subject:              subject,
    body:                 body,
    yahoo_transaction_id: extractAuctionId_(subject, body),
    transaction_url:      extractTransactionUrl_(body),
    body_hash:            computeBodyHash_(subject, receivedIso, body)
  };
}

// オークションIDの抽出（3段フォールバック）: URLの aid= → 本文ラベル → 件名末尾(...)
// 例: f1227209818（先頭1文字 + 数字9〜11桁。数字のみのIDもあり得る）
function extractAuctionId_(subject, body) {
  var m;
  m = body.match(/[?&]aid=([A-Za-z0-9]+)/);
  if (m) return m[1];
  m = body.match(/オークションID[:：]\s*([A-Za-z0-9]+)/);
  if (m) return m[1];
  m = subject.match(/\(([A-Za-z0-9]+)\)\s*$/);
  if (m) return m[1];
  return '';
}

// 本文取得: getPlainBody() を優先。空ならHTML本文をタグ除去してフォールバック。
// （ヤフオク通知は通常プレーンテキストだが、将来HTML化された場合の保険）
function getBodyText_(m) {
  var plain = m.getPlainBody() || '';
  if (plain.replace(/\s/g, '').length > 0) return plain;
  var html = m.getBody() || '';
  return stripHtml_(html);
}

function stripHtml_(html) {
  return html
    .replace(/<\s*br\s*\/?\s*>/gi, '\n')
    .replace(/<\/(p|div|tr|li)\s*>/gi, '\n')
    .replace(/<[^>]+>/g, '')
    .replace(/&nbsp;/gi, ' ')
    .replace(/&amp;/gi, '&')
    .replace(/&lt;/gi, '<')
    .replace(/&gt;/gi, '>')
    .replace(/[ \t]+\n/g, '\n')
    .trim();
}

// 取引ナビURLの抽出: contact.auctions.yahoo.co.jp 配下のURLを本文から取得。
// 例: https://contact.auctions.yahoo.co.jp/seller/top?aid=...&oid=...&read=...
function extractTransactionUrl_(body) {
  var m = body.match(/https?:\/\/contact\.auctions\.yahoo\.co\.jp\/\S+/);
  if (m) return m[0].replace(/[)\]>,。]+$/, '');  // 末尾の閉じ括弧/句点を除去
  return '';
}

// body_hash の正規定義（backend もこの順序・区切りで再計算すれば一致する）:
//   SHA256( subject + "\n" + received_at(ISO) + "\n" + body ) を小文字hexで
function computeBodyHash_(subject, receivedIso, body) {
  return sha256Hex_(subject + '\n' + receivedIso + '\n' + body);
}

function sha256Hex_(str) {
  var bytes = Utilities.computeDigest(
    Utilities.DigestAlgorithm.SHA_256, str, Utilities.Charset.UTF_8);
  var hex = '';
  for (var i = 0; i < bytes.length; i++) {
    var b = (bytes[i] + 256) % 256;
    var h = b.toString(16);
    hex += (h.length === 1 ? '0' + h : h);
  }
  return hex;
}

// "2026-06-01T00:00:00+09:00" → "2026/06/01"
function toGmailAfter_(iso) {
  if (!iso) return '';
  var m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})/);
  return m ? (m[1] + '/' + m[2] + '/' + m[3]) : '';
}

function nowIso_() {
  return new Date().toISOString();
}

function jsonOutput_(obj) {
  return ContentService
    .createTextOutput(JSON.stringify(obj))
    .setMimeType(ContentService.MimeType.JSON);
}
