/**
 * セルの編集後、IDからアニメ情報を取得し、
 * その後、取得したIDを別のシートにコピーする処理を行います。
 */
const ANNICT_API_TOKEN = 'eMDFpHVaI2Fw00NPAp1Lz3cvB4QhXFmPuMTECLGQt-0'; //後でpropertiesに変更
const ANNICT_GRAPHQL_ENDPOINT = 'https://api.annict.com/graphql';
const url = PropertiesService.getScriptProperties().getProperty('PYTHON_API_URL'); //
const ALLOWED_SHEET_NAMES = ['2025', 'DATE']; // スクリプトの実行を許可するシート名を設定
const ID_COPY_TARGET_SHEET_NAME = 'DATE'; // IDをコピー先のシート名

// スクリプトが紐づいているスプレッドシートを事前に取得しておく（Webアプリ実行時の挙動を安定させるため）
const SPREADSHEET = SpreadsheetApp.getActiveSpreadsheet();

/**
* スプレッドシートの編集時に自動的に実行される関数です。
* 指定されたシート（ALLOWED_SHEET_NAMES）のI列にアニメIDが入力されたら、
* そのアニメの情報を取得してC, J, K, L, M列に表示し、B列に編集日を記入します。
* その後、IDをコピーします。
* D列が編集された場合は、Sparkline記入用のダイアログを表示します。
*/
/**
* スプレッドシートの編集時に自動的に実行される関数です。
* 指定されたシート（ALLOWED_SHEET_NAMES）のI列にアニメIDが入力されたら、
* そのアニメの情報を取得してC, J, K, L, M列に表示し、B列に編集日を記入します。
* その後、IDをコピーします。
* D列が編集された場合は、Sparkline記入用のダイアログを表示します。
*/
function onEdit(e) {
  const range = e.range;
  const sheet = range.getSheet();
  const sheetName = sheet.getName(); // シート名を取得
  const column = range.getColumn();
  const row = range.getRow();
  const editedValue = String(range.getValue()).trim(); // 編集後のセルの値を取得（D列の処理で利用）

  // I列 (column 9) の処理
  if (ALLOWED_SHEET_NAMES.includes(sheetName) && sheetName !== ID_COPY_TARGET_SHEET_NAME &&
      column === 9 && // I列 (column 9)
      row >= 3 // 3行目以降 (row >= 3)
  ) {
    const annictId = editedValue; // I列の場合はannictIdとして利用

    // I列にIDが入力された場合
    if (annictId.length > 0 && !isNaN(annictId)) { // 数字であることを確認

      // C列のタイトルが既に存在するか、無効な値でないかを確認
      const existingCColumnValue = sheet.getRange(row, 3).getValue(); // C列のタイトルを確認
      if (!existingCColumnValue || String(existingCColumnValue).trim() === "" || String(existingCColumnValue).trim() === "情報が見つかりませんでした" || String(existingCColumnValue).trim() === "一致する新規情報が見つかりませんでした") {
        processSheetRowWithId(sheet, row, annictId); // IDから情報を取得してシートに記入

        // B列に編集日を記入 (同じ日がない場合のみ)
        const today = Utilities.formatDate(new Date(), SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(), "yyyy/MM/dd");
        const lastRowInB = sheet.getLastRow();
        let bColumnValues = [];
        if (lastRowInB >= 3) {
          bColumnValues = sheet.getRange("B3:B" + lastRowInB).getValues().flat().map(date =>
            date instanceof Date ? Utilities.formatDate(date, SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(), "yyyy/MM/dd") : String(date)
          );
        }

        if (!bColumnValues.includes(today)) {
          sheet.getRange(row, 2).setValue(today); // B列に日付を記入
          Logger.log(`[onEdit] 行 ${row} のB列に本日(${today})を記入しました。`);
        } else {
          Logger.log(`[onEdit] B列に本日(${today})が既に存在するため、日付の記入をスキップしました。`);
        }

        SpreadsheetApp.flush(); // 保留中の変更を強制的に適用
      } else {
        Logger.log(`[onEdit] 行 ${row} のC列には既に情報があるため、API呼び出しをスキップしました。`);
      }

      // I列にIDを記入した直後にDATEシートへコピー
      copyIdsFromLargestNumberRowToDate(sheet);
      // JSON送信処理
      const aColumnValue = sheet.getRange(row, 1).getValue(); // A列のデータを取得
      const cColumnValue = sheet.getRange(row, 3).getValue(); // C列のデータを取得
      let messageToSend;
      if (aColumnValue) { // A列にデータがある場合
        messageToSend = `${aColumnValue}ふさ目 開催\n${cColumnValue}`;
      } else { // A列にデータがない場合
        messageToSend = `${cColumnValue}`;
      }

      sendDataToPython({
        type: "id_entry_notification",
        message: messageToSend,
        a_column_data: aColumnValue, // 必要であれば個別のデータも送る
        c_column_data: cColumnValue, // 必要であれば個別のデータも送る
        timestamp: new Date().toISOString()
      });

      Logger.log(`[onEdit] Python APIにID記入通知を送信しました: "${messageToSend}"`);
    }
    // I列のIDがクリアされた場合
    else if (annictId.length === 0) {
      // C列、J列からM列もクリアする
      sheet.getRange(row, 3).clearContent(); // C列をクリア
      sheet.getRange(row, 10, 1, 4).clearContent(); // J列からM列までをクリア (4項目分)
      sheet.getRange(row, 2).clearContent(); // B列もクリア

      SpreadsheetApp.flush(); // 保留中の変更を強制的に適用

      // ★★★ I列クリア時のDATEシートの全データクリア処理（2行目以降）を追加 ★★★
      const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(ID_COPY_TARGET_SHEET_NAME);
      if (dateSheet) {
        const lastRowDate = dateSheet.getLastRow();
        const startRowDate = 2; // DATEシートのデータは2行目から始まる想定
        if (lastRowDate >= startRowDate) {
          // 2行目から最終行まで、全列をクリア
          dateSheet.getRange(startRowDate, 1, lastRowDate - startRowDate + 1, dateSheet.getLastColumn()).clearContent();
          Logger.log(`[onEdit] I列クリアに伴い、DATEシートの全データ（2行目から最終行まで）をクリアしました。`);
        }
      }
    }

    // I列にIDが入力された場合にのみ、ステータス取得と2025シート更新を実行
    if (annictId.length > 0 && !isNaN(annictId)) {
      getAnimeViewerStatusForMultipleUsers();
      updateTestSheetMaxId();
    }
  }

  // --- D列 (column 4) の処理 ---
  // ALLOWED_SHEET_NAMESに含まれるシートのD列（column 4）の2行目以降が編集された場合に処理を実行
  if (column === 4 && row >= 2 && ALLOWED_SHEET_NAMES.includes(sheetName)) {
    if (editedValue.length > 0) { // D列に何らかの値が入力された場合
      showSparklineDialog(); // ダイアログを表示
    } else { // D列のセルがクリアされた場合 (値が空の場合)
      // ★★★ ここを修正 ★★★
      clearSparklinesFromDateSheet(); // DATEシートのスパークラインをクリア
      Logger.log(`[onEdit] D列のセルがクリアされたため、DATEシートのスパークラインをクリアしました。`);
      // ★★★ ここまで修正 ★★★
    }
  }
}

/**
 * ダイアログを表示します。
 */
function showSparklineDialog() {
  const htmlOutput = HtmlService.createHtmlOutputFromFile('dialog')
      .setWidth(520)
      .setHeight(520);
  SpreadsheetApp.getUi().showModalDialog(htmlOutput, '誰が見る？');
}

/**
 * DATEシートのC列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineC() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase(); // 比較用に大文字化
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  // C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行を見つける
  // getLastRow() が1の場合（ヘッダーのみの場合）も対応
  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues(); // 表示されている値
  const formulasToScan = rangeToScan.getFormulas(); // 数式

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    // C, D, E列のいずれかに値またはスパークラインの数式があるかチェック
    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    // C, D, E列の全てが空白であり、かつスパークラインも含まない場合
    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break; // 最初の完全に空の行が見つかったらループを抜ける
    }
    if (i === valuesToScan.length - 1) { // 最後の行までチェックして全て埋まっていた場合
      targetRow = lastRow + 1; // 最終行の次の行に設定
    }
  }
  // シートが空の場合 (lastRow < startRow) は targetRow は startRow (2) のまま

  // 記入
  dateSheet.getRange(targetRow, 3).setFormula(sparklineFormula);
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのC${targetRow}に${sparklineFormula}を記入しました。`);
  return `C列に記入しました。(行 ${targetRow})`;
}

/**
 * DATEシートのD列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineD() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase();
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues();
  const formulasToScan = rangeToScan.getFormulas();

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break;
    }
    if (i === valuesToScan.length - 1) {
      targetRow = lastRow + 1;
    }
  }

  // 記入
  dateSheet.getRange(targetRow, 4).setFormula(sparklineFormula);
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのD${targetRow}に${sparklineFormula}を記入しました。`);
  return `D列に記入しました。(行 ${targetRow})`;
}

/**
 * DATEシートのE列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineE() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase();
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues();
  const formulasToScan = rangeToScan.getFormulas();

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break;
    }
    if (i === valuesToScan.length - 1) {
      targetRow = lastRow + 1;
    }
  }

  // 記入
  dateSheet.getRange(targetRow, 5).setFormula(sparklineFormula);
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのE${targetRow}に${sparklineFormula}を記入しました。`);
  return `E列に記入しました。(行 ${targetRow})`;
}
/**
 * DATEシートのD列とE列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineDE() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase();
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues();
  const formulasToScan = rangeToScan.getFormulas();

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break;
    }
    if (i === valuesToScan.length - 1) {
      targetRow = lastRow + 1;
    }
  }

  // 記入
  dateSheet.getRange(targetRow, 4).setFormula(sparklineFormula); // D列に記入
  dateSheet.getRange(targetRow, 5).setFormula(sparklineFormula); // E列に記入
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのD${targetRow}とE${targetRow}に${sparklineFormula}を記入しました。`);
  return `D列とE列に記入しました。(行 ${targetRow})`;
}
/**
 * DATEシートのC列とE列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineCE() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase();
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues();
  const formulasToScan = rangeToScan.getFormulas();

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break;
    }
    if (i === valuesToScan.length - 1) {
      targetRow = lastRow + 1;
    }
  }

  // 記入
  dateSheet.getRange(targetRow, 3).setFormula(sparklineFormula); // C列に記入
  dateSheet.getRange(targetRow, 5).setFormula(sparklineFormula); // E列に記入
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのC${targetRow}とE${targetRow}に${sparklineFormula}を記入しました。`);
  return `C列とE列に記入しました。(行 ${targetRow})`;
}
/**
 * DATEシートのC列とD列に=SPARKLINE({1,0})を記入します。
 * C, D, E列の全てが空白（またはスパークラインも含まない）である最初の行に記入します。
 */
function insertSparklineCD() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    SpreadsheetApp.getUi().alert('エラー', '「DATE」という名前のシートが見つかりません。', SpreadsheetApp.getUi().ButtonSet.OK);
    return;
  }

  const sparklineFormula = '=SPARKLINE({1,0})';
  const SPARKLINE_CHECK_FORMULA = sparklineFormula.toUpperCase();
  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  let targetRow = startRow; // 記入対象行の初期値

  const rangeToScan = dateSheet.getRange(startRow, 3, Math.max(1, lastRow - startRow + 1), 3); // C列からE列まで
  const valuesToScan = rangeToScan.getValues();
  const formulasToScan = rangeToScan.getFormulas();

  for (let i = 0; i < valuesToScan.length; i++) {
    const rowValues = valuesToScan[i];
    const rowFormulas = formulasToScan[i];

    const hasCDataOrSparkline = (String(rowValues[0]).trim() !== '' || String(rowFormulas[0]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasDDataOrSparkline = (String(rowValues[1]).trim() !== '' || String(rowFormulas[1]).toUpperCase() === SPARKLINE_CHECK_FORMULA);
    const hasEDataOrSparkline = (String(rowValues[2]).trim() !== '' || String(rowFormulas[2]).toUpperCase() === SPARKLINE_CHECK_FORMULA);

    if (!hasCDataOrSparkline && !hasDDataOrSparkline && !hasEDataOrSparkline) {
      targetRow = startRow + i;
      break;
    }
    if (i === valuesToScan.length - 1) {
      targetRow = lastRow + 1;
    }
  }

  // 記入
  dateSheet.getRange(targetRow, 3).setFormula(sparklineFormula); // C列に記入
  dateSheet.getRange(targetRow, 4).setFormula(sparklineFormula); // D列に記入
  SpreadsheetApp.flush();
  Logger.log(`DATEシートのC${targetRow}とD${targetRow}に${sparklineFormula}を記入しました。`);
  return `C列とD列に記入しました。(行 ${targetRow})`;
}

/**
 * ダイアログを閉じます。
 */
function closeDialog() {
  // この関数は、dialog.htmlから呼び出され、ダイアログを閉じます。
  // google.script.host.close() はdialog.htmlのスクリプト内で直接呼び出すのが一般的ですが、
  // GAS側で特別な処理が必要な場合はこのように定義できます。
  // 今回のケースではdialog.html側で直接close()を呼び出しているので、この関数は必須ではありませんが、
  // 必要に応じて残しておいても問題ありません。
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * Web APIとして公開され、外部からトリガーされる関数です。
 * 各シートのA列の最大値の行、およびその行以降でI列にIDがある場合に情報を取得し、IDをコピーします。
 * その後、ユーザーの視聴ステータスを取得します。
 */
function doPost(e) {
  Logger.log("[doPost] リクエストを受信しました。メイン処理を開始します。");

  let mainProcessSuccess = true;
  let mainProcessMessage = "メイン処理が完了しました。";

  try {
    let processedSheetsCount = 0;
    let skippedSheetsCount = 0;

    // Annict API処理の対象は'2025'シートのみに限定
    const targetSheetNameForAnnictApi = '2025';
    const targetSheet = SPREADSHEET.getSheetByName(targetSheetNameForAnnictApi);

    if (!targetSheet) {
      Logger.log(`[doPost] ターゲットシート「${targetSheetNameForAnnictApi}」が見つかりません。処理をスキップします。`);
      mainProcessSuccess = false;
      mainProcessMessage = `ターゲットシート「${targetSheetNameForAnnictApi}」が見つかりません。`;
    } else {
      Logger.log(`[doPost] シート「${targetSheetNameForAnnictApi}」のAnnict情報取得処理を開始します。`);

      const lastRow = targetSheet.getLastRow();
      if (lastRow < 3) {
        Logger.log(`[doPost] シート「${targetSheetNameForAnnictApi}」に処理対象のデータがありません (最終行: ${lastRow})`);
        skippedSheetsCount++;
      } else {
        // A列からI列までの全データをまとめて取得
        const dataRange = targetSheet.getRange(3, 1, lastRow - 2, 9);
        const allData = dataRange.getValues();

        for (let i = 0; i < allData.length; i++) {
          const currentRow = i + 3; // スプレッドシートの絶対行番号
          const annictId = String(allData[i][8] || '').trim(); // I列の値 (0-indexedで8番目)
          const aColumnValue = String(allData[i][0] || '').trim(); // A列の値 (0-indexedで0番目)
          const cColumnValue = String(allData[i][2] || '').trim(); // C列の値 (0-indexedで2番目)

          // I列にIDがあり、かつC列が空白または特定のエラーメッセージの場合のみAPIを叩く
          if (annictId.length > 0 && !isNaN(annictId)) {
            if (!cColumnValue || cColumnValue === "" || cColumnValue === "情報が見つかりませんでした" || cColumnValue === "一致する新規情報が見つかりませんでした") {
              processSheetRowWithId(targetSheet, currentRow, annictId); // IDから情報を取得してシートに記入

              // B列に編集日を記入 (同じ日がない場合のみ)
              const today = Utilities.formatDate(new Date(), SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(), "yyyy/MM/dd");
              const bColumnCell = targetSheet.getRange(currentRow, 2);
              const existingBValue = bColumnCell.getValue();
              const existingBDate = existingBValue instanceof Date ? Utilities.formatDate(existingBValue, SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(), "yyyy/MM/dd") : String(existingBValue);

              if (existingBDate !== today) {
                bColumnCell.setValue(today); // B列に日付を記入
                Logger.log(`[doPost] 行 ${currentRow} のB列に本日(${today})を記入しました。`);
              } else {
                Logger.log(`[doPost] B列に本日(${today})が既に存在するため、日付の記入をスキップしました。(行 ${currentRow})`);
              }

              // Python APIへのデータ送信
              let messageToSend;
              if (aColumnValue) {
                messageToSend = `${aColumnValue}ふさ目 開催\n${targetSheet.getRange(currentRow, 3).getValue()}`; // C列の最新値を取得
              } else {
                messageToSend = `${targetSheet.getRange(currentRow, 3).getValue()}`; // C列の最新値を取得
              }
              sendDataToPython({
                type: "id_entry_notification",
                message: messageToSend,
                a_column_data: aColumnValue,
                c_column_data: targetSheet.getRange(currentRow, 3).getValue(),
                timestamp: new Date().toISOString()
              });
              Logger.log(`[doPost] Python APIにID記入通知を送信しました: "${messageToSend}"`);

            } else {
              Logger.log(`[doPost] シート「${targetSheetNameForAnnictApi}」行 ${currentRow} のC列には既に情報があるため、API呼び出しをスキップしました。`);
            }
          }
          // I列のIDがクリアされた場合の処理
          else if (annictId.length === 0 && (cColumnValue.length > 0 || targetSheet.getRange(currentRow, 10).getValue())) {
            // I列が空で、かつC列またはJ列に何かデータがある場合のみクリア
            targetSheet.getRange(currentRow, 3).clearContent(); // C列をクリア
            targetSheet.getRange(currentRow, 10, 1, 4).clearContent(); // J列からM列までをクリア (4項目分)
            targetSheet.getRange(currentRow, 2).clearContent(); // B列もクリア
            Logger.log(`[doPost] シート「${targetSheetNameForAnnictApi}」行 ${currentRow} のI列が空のため、関連列をクリアしました。`);

            // I列クリア時のDATEシートの全データクリア処理（2行目以降）
            const dateSheet = SPREADSHEET.getSheetByName(ID_COPY_TARGET_SHEET_NAME);
            if (dateSheet) {
                const lastRowDate = dateSheet.getLastRow();
                const startRowDate = 2; // DATEシートのデータは2行目から始まる想定
                if (lastRowDate >= startRowDate) {
                    dateSheet.getRange(startRowDate, 1, lastRowDate - startRowDate + 1, dateSheet.getLastColumn()).clearContent();
                    Logger.log(`[doPost] I列クリアに伴い、DATEシートの全データ（2行目から最終行まで）をクリアしました。`);
                }
            }
          }
        }
        processedSheetsCount++;
        Logger.log(`[doPost] シート「${targetSheetNameForAnnictApi}」のAnnict情報取得処理が完了しました。`);
      }
      SpreadsheetApp.flush(); // ここで書き込みを確定
    }

    // Annict API処理完了後、DATEシートへのIDコピーを実行
    Logger.log(`[doPost] Annict情報取得処理が完了しました。情報元シート「${targetSheetNameForAnnictApi}」からIDをコピーします。`);
    // '2025'シートをコピー元として渡す
    copyIdsFromLargestNumberRowToDate(targetSheet);

    Logger.log(`[doPost] メイン処理完了。${processedSheetsCount} シート処理済み、${skippedSheetsCount} シートスキップ。`);

  } catch (error) {
    Logger.log(`[doPost] メイン処理中にエラーが発生しました: ${error.message}`);
    mainProcessSuccess = false;
    mainProcessMessage = `メイン処理中にエラーが発生しました: ${error.message}`;
  }

  // --- IDコピー処理が完了した後、視聴ステータス取得関数を実行 ---
  Logger.log("[doPost] 視聴ステータス取得処理を開始します。");
  const statusResult = getAnimeViewerStatusForMultipleUsers();
  Logger.log(`[doPost] 視聴ステータス取得処理が完了しました。結果: ${JSON.stringify(statusResult)}`);

  // --- 全てwatchedの場合に2025シートを更新する処理を実行 ---
  Logger.log("[doPost] updateTestSheetMaxId を実行します。");
  updateTestSheetMaxId();

  // JSON形式でレスポンスを返す
  return ContentService.createTextOutput(JSON.stringify({
      mainProcess: { success: mainProcessSuccess, message: mainProcessMessage },
      viewerStatusProcess: statusResult
    }))
    .setMimeType(ContentService.MimeType.JSON);
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * 指定されたシートの指定された行のデータを処理する共通関数。
 * IDからAnnict情報を取得し、シートに記入します。
 * @param {GoogleAppsScript.Spreadsheet.Sheet} sheet 処理対象のシートオブジェクト
 * @param {number} row 処理対象の行番号
 * @param {string} annictId 検索対象のアニメID
 */
function processSheetRowWithId(sheet, row, annictId) {
  const work = searchAnnictAnimeByIdGraphQL(annictId);

  if (work) {
    const outputData = extractWorkData(work);
    // C列にタイトル、J列からM列に情報を記入
    sheet.getRange(row, 3).setValue(outputData[0]); // C列にタイトル
    sheet.getRange(row, 10, 1, 4).setValues([outputData.slice(1)]); // J列からM列にメディア、年数、季節、話数
    Logger.log(`[processSheetRowWithId] ID "${annictId}" の作品情報が見つかりました: ${work.title}`);
  } else {
    sheet.getRange(row, 3).setValue("情報が見つかりませんでした"); // C列にエラーメッセージ
    sheet.getRange(row, 10, 1, 4).clearContent(); // J列からM列をクリア
    Logger.log(`[processSheetRowWithId] ID "${annictId}" のアニメ情報が見つかりませんでした。`);
  }
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * Annict GraphQL APIからアニメ情報をIDで検索し、単一の候補を返します。
 * エラーの場合はnullを返します。
 */
function searchAnnictAnimeByIdGraphQL(annictId) {
  const query = `
    query SearchWorkById($id: Int!) {
      searchWorks(annictIds: [$id]) {
        nodes {
          annictId
          title
          media
          seasonYear
          seasonName
          episodesCount
        }
      }
    }
  `;

  const variables = {
    id: parseInt(annictId, 10)
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: `Bearer ${ANNICT_API_TOKEN}`,
    },
    payload: JSON.stringify({
      query,
      variables
    }),
  };

  try {
    const response = UrlFetchApp.fetch(ANNICT_GRAPHQL_ENDPOINT, options);
    const data = JSON.parse(response.getContentText());

    if (data.errors) {
      Logger.log("GraphQLエラー (ID検索): " + JSON.stringify(data.errors));
      return null;
    }

    // ID検索なので、最初の要素を返す
    return data.data.searchWorks.nodes && data.data.searchWorks.nodes.length > 0 ? data.data.searchWorks.nodes[0] : null;
  } catch (e) {
    Logger.log("Annict GraphQL API (ID検索) からの情報取得中にエラーが発生しました: " + e.message);
    return null;
  }
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * ワークオブジェクトから必要なデータのみを抽出し、配列形式で返します。
 * [タイトル, メディア, 年数, 季節, 話数] の順序で返します。
 */
function extractWorkData(work) {
  return [
    work.title || '',
    work.media || '',
    work.seasonYear || '',
    work.seasonName || '',
    work.episodesCount || ''
  ];
}

/**
 * コピー元シートのA列の最大値の行から下のI列のIDとC列のタイトルを、
 * ターゲットシート (ID_COPY_TARGET_SHEET_NAME) のA列にタイトル、B列にIDとしてコピーする関数。
 * @param {GoogleAppsScript.Spreadsheet.Sheet} sourceSheet ID取得元のシートオブジェクト
 */
function copyIdsFromLargestNumberRowToDate(sourceSheet) {
  // IDを読み取る前に、念のため最新のデータが反映されていることを保証
  SpreadsheetApp.flush();

  const targetSheet = SPREADSHEET.getSheetByName(ID_COPY_TARGET_SHEET_NAME);
  if (!targetSheet) {
    Logger.log(`[copyIdsFromLargestNumberRowToDate] ターゲットシート「${ID_COPY_TARGET_SHEET_NAME}」が見つかりません。`);
    return;
  }

  const lastRow = sourceSheet.getLastRow();
  if (lastRow < 3) {
    Logger.log(`[copyIdsFromLargestNumberRowToDate] コピー元シート「${sourceSheet.getName()}」にデータがありません (3行目以降)。`);
    // タイトルとIDの貼り付け先をクリア
    targetSheet.getRange("A2:B").clearContent();
    return;
  }

  // A列の最大値の行を特定する
  const aColumnRange = sourceSheet.getRange(3, 1, lastRow - 2, 1); // A3から最終行まで
  const aColumnData = aColumnRange.getValues();

  let maxNumber = -Infinity;
  let startRowToCopy = -1; // コピーを開始するスプレッドシートの絶対行番号

  for (let i = 0; i < aColumnData.length; i++) {
    const cellValue = aColumnData[i][0];
    const currentRowAbsolute = i + 3; // スプレッドシートの行番号 (3行目から始まるため +3)

    if (typeof cellValue === 'number' && !isNaN(cellValue) && cellValue > maxNumber) {
      maxNumber = cellValue;
      startRowToCopy = currentRowAbsolute;
    }
  }

  if (startRowToCopy === -1) {
    Logger.log(`[copyIdsFromLargestNumberRowToDate] コピー元シート「${sourceSheet.getName()}」のA列に有効な数字が見つかりませんでした。`);
    // タイトルとIDの貼り付け先をクリア
    targetSheet.getRange("A2:B").clearContent();
    return;
  }

  // 最大値が見つかった行（startRowToCopy）のC列（タイトル）とI列（ID）から最終行までをコピー対象とする
  const dataToCopy = [];
  // C列 (3番目の列) と I列 (9番目の列) からデータを取得
  // getRange(row, column, numRows, numColumns)
  // C列からI列まで (7列分) のデータを取得し、その中からCとIのインデックスを抽出
  const sourceRangeToCopy = sourceSheet.getRange(startRowToCopy, 3, lastRow - startRowToCopy + 1, 7); // C列からI列まで
  const sourceData = sourceRangeToCopy.getValues();

  sourceData.forEach(row => {
    const title = String(row[0] || '').trim(); // C列の値 (0-indexedで0番目)
    const annictId = String(row[6] || '').trim(); // I列の値 (0-indexedで6番目)

    if (annictId !== "" && !isNaN(annictId) && title !== "") { // IDとタイトルが有効な場合のみコピー対象
      dataToCopy.push([title, annictId]); // [タイトル, ID] の順で2次元配列としてプッシュ
    } else if (annictId !== "" || title !== "") { // どちらか片方だけ有効な場合はログを出す
      Logger.log(`[copyIdsFromLargestNumberRowToDate] 無効なデータが見つかりました（C列: ${title}, I列: ${annictId}）。スキップします。`);
    }
  });

  // 既存のデータをクリアしてから貼り付ける
  targetSheet.getRange("A2:B").clearContent(); // A2以降とB2以降をクリア

  if (dataToCopy.length > 0) {
    // A2からタイトル、B2からIDを貼り付け
    targetSheet.getRange(2, 1, dataToCopy.length, 2).setValues(dataToCopy);
    Logger.log(`[copyIdsFromLargestNumberToDate] ${dataToCopy.length} 件のタイトルとIDを「${ID_COPY_TARGET_SHEET_NAME}」シートにコピーしました。`);
  } else {
    Logger.log(`[copyIdsFromLargestNumberToDate] コピー元シート「${sourceSheet.getName()}」のC列とI列に、A列最大値の行以降でコピーする有効なタイトルとIDが見つかりませんでした。`);
  }
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * スプレッドシートの指定されたシートのB列にあるIDを取得し、
 * 複数のAnnictトークン（スクリプトプロパティに設定）を使用して、
 * それぞれC列、D列、E列に視聴ステータスを記載します。
 * C列、D列、E列の既存ステータスが「watched」の場合、またはセルにスパークラインが記入されている場合はAPI呼び出しをスキップします。
 * 取得したステータスが「watched」だった場合は、実行ログにその旨を記載します。
 * データシート名もスクリプトプロパティから取得します。
 */
function getAnimeViewerStatusForMultipleUsers() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  // ★★★ シート名をスクリプトプロパティから取得 ★★★
  const sheetName = PropertiesService.getScriptProperties().getProperty('SHEET_NAME');
  if (!sheetName) {
    console.error("エラー: スクリプトプロパティ 'SHEET_NAME' が設定されていません。");
    return { success: false, message: "エラー: スクリプトプロパティ 'SHEET_NAME' が設定されていません。" };
  }

  const sheet = spreadsheet.getSheetByName(sheetName);

  if (!sheet) {
    console.error(`エラー: 指定されたシート「${sheetName}」が見つかりませんでした。シート名を確認してください。`);
    return { success: false, message: `エラー: 指定されたシート「${sheetName}」が見つかりませんでした。` };
  }

  // C1, D1, E1 からユーザー名を取得
  const user1Name = sheet.getRange("C1").getValue();
  const user2Name = sheet.getRange("D1").getValue();
  const user3Name = sheet.getRange("E1").getValue();

  const userTokens = [
    { token: PropertiesService.getScriptProperties().getProperty('ANNICT_TOKEN_USER1'), outputColumn: 3, name: user1Name }, // C列 (3)
    { token: PropertiesService.getScriptProperties().getProperty('ANNICT_TOKEN_USER2'), outputColumn: 4, name: user2Name }, // D列 (4)
    { token: PropertiesService.getScriptProperties().getProperty('ANNICT_TOKEN_USER3'), outputColumn: 5, name: user3Name } // E列 (5)
  ];

  // A列のタイトル、B列のID、C,D,E列の既存ステータスを取得
  // ヘッダー行を考慮して、2行目から最終行までを取得
  const startRow = 2; // ヘッダーが1行目にあると仮定し、データは2行目から始まる
  const titleColumn = 1; // A列
  const idColumn = 2; // B列
  const lastRow = sheet.getLastRow();

  if (lastRow < startRow) {
    console.warn("警告: データシートにIDが見つかりません。");
    return { success: true, message: "データシートにIDが見つかりませんでした。" };
  }

  // 取得する範囲はA列（タイトル）から最大でE列（出力列）まで
  const rangeWidth = Math.max(...userTokens.map(u => u.outputColumn)) - titleColumn + 1;
  const dataRange = sheet.getRange(startRow, titleColumn, lastRow - startRow + 1, rangeWidth);
  const values = dataRange.getValues(); // セルの表示値を取得
  const formulas = dataRange.getFormulas(); // セルの数式を取得

  // GraphQLクエリ
  const graphqlQuery = `
    query GetViewerStatus($ids: [Int!]) {
      searchWorks(annictIds: $ids) {
        nodes {
          annictId
          title
          viewerStatusState
        }
      }
    }
  `;

  let overallSuccess = true;
  const results = {};

  userTokens.forEach((user, userIndex) => {
    const ACCESS_TOKEN = user.token;
    const outputColumn = user.outputColumn;
    const userName = user.name || `ユーザー${userIndex + 1}`; // C1/D1/E1が空の場合のフォールバック
    results[userName] = { processedCount: 0, skippedWatched: 0, skippedSparkline: 0, errors: 0 };

    if (!ACCESS_TOKEN) {
      console.error(`エラー: スクリプトプロパティ 'ANNICT_TOKEN_USER${userIndex + 1}' が設定されていません。このユーザーの処理をスキップします。`);
      // 全てのIDに対して「トークン未設定」を書き込む
      for (let i = 0; i < values.length; i++) {
        sheet.getRange(startRow + i, outputColumn).setValue("トークン未設定");
      }
      overallSuccess = false;
      results[userName].message = "トークン未設定";
      return;
    }

    console.log(`\n--- ${userName} (${ACCESS_TOKEN.substring(0, 5)}...) の視聴ステータスを取得中 ---`);

    for (let i = 0; i < values.length; i++) {
      const animeTitle = values[i][0]; // A列のタイトル
      const animeId = values[i][1]; // B列のID
      
      // 既存ステータスの列インデックスを計算 (values/formulas配列は取得したdataRangeの相対的なインデックスを持つ)
      const existingStatusColumnIndex = outputColumn - titleColumn;
      const existingStatusValue = values[i][existingStatusColumnIndex]; // セルの表示値
      const existingStatusFormula = formulas[i][existingStatusColumnIndex]; // セルの数式

      if (typeof animeId === 'number' && Number.isInteger(animeId) && animeId > 0) {
        // スパークラインの数式チェック
        const isSparkline = typeof existingStatusFormula === 'string' && existingStatusFormula.toUpperCase().includes('=SPARKLINE(');
        
        // 既存ステータスが "watched" でない、かつスパークラインが記入されていない場合にのみAPIを呼び出す
        if (isSparkline) {
          console.log(` ID '${animeId}' は既にスパークラインが記入されているためスキップします。`);
          results[userName].skippedSparkline++;
        } else if (typeof existingStatusValue === 'string' && existingStatusValue.toLowerCase() === 'watched') {
          console.log(` ID '${animeId}' は既に「watched」のためスキップします。`);
          results[userName].skippedWatched++;
        } else { // watchedでもスパークラインでもない場合のみAPI呼び出し
          const headers = {
            "Authorization": `Bearer ${ACCESS_TOKEN}`,
            "Content-Type": "application/json"
          };
          const variables = {
            "ids": [animeId]
          };
          const options = {
            "method": "post",
            "headers": headers,
            "payload": JSON.stringify({
              query: graphqlQuery,
              variables: variables
            }),
            "muteHttpExceptions": true
          };

          console.log(` ID '${animeId}' のアニメの視聴ステータスを取得中...`);

          try {
            const response = UrlFetchApp.fetch(ANNICT_GRAPHQL_ENDPOINT, options);
            const responseCode = response.getResponseCode();
            const responseText = response.getContentText();

            if (responseCode >= 400) {
              console.error(` APIリクエスト中にエラーが発生しました: HTTP ${responseCode}`);
              console.error(` レスポンス内容: ${responseText}`);
              sheet.getRange(startRow + i, outputColumn).setValue(`APIエラー: HTTP ${responseCode}`);
              results[userName].errors++;
              overallSuccess = false;
            } else {
              const result = JSON.parse(responseText);

              if (result && result.errors) {
                console.error(" GraphQLエラーが発生しました:");
                result.errors.forEach(error => console.error(` - ${error.message || '不明なエラー'}`));
                sheet.getRange(startRow + i, outputColumn).setValue("GraphQLエラー");
                results[userName].errors++;
                overallSuccess = false;
              } else {
                const foundWorks = result.data?.searchWorks?.nodes;
                if (foundWorks && foundWorks.length > 0) {
                  const viewerStatus = foundWorks[0].viewerStatusState;
                  if (viewerStatus) {
                    console.log(` 取得ステータス: ${viewerStatus}`);
                    sheet.getRange(startRow + i, outputColumn).setValue(viewerStatus);
                    results[userName].processedCount++;
                    if (viewerStatus.toLowerCase() === 'watched') {
                      console.log(` ログ: ID '${animeId}' のステータスを "watched" に更新しました。`);
                      // ★★★ Python APIへのJSON送信処理 ★★★
                      const message = `${userName} ${animeTitle} 観了`; // ユーザー名 A列データ 観了 の形式
                      sendDataToPython({
                        type: "watched_status_update",
                        user_name: userName,
                        anime_title: animeTitle,
                        message: message,
                        timestamp: new Date().toISOString()
                      });
                      // ★★★ ここまで変更 ★★★
                    }
                  } else {
                    console.log(" 視聴ステータス情報が見つかりませんでした。");
                    sheet.getRange(startRow + i, outputColumn).setValue("ステータスなし / 権限不足");
                    results[userName].errors++;
                    overallSuccess = false;
                  }
                } else {
                  console.log(` ID '${animeId}' に一致する作品が見つかりませんでした。`);
                  sheet.getRange(startRow + i, outputColumn).setValue("作品見当たらず");
                  results[userName].errors++;
                  overallSuccess = false;
                }
              }
            }
          } catch (e) {
            console.error(` リクエストまたはJSONパース中にエラーが発生しました: ${e.message}`);
            sheet.getRange(startRow + i, outputColumn).setValue(`スクリプトエラー: ${e.message}`);
            results[userName].errors++;
            overallSuccess = false;
          }
          // Utilities.sleep(500); // 必要であればAPIリクエスト間の間隔を開ける
        }
      } else {
        console.warn(` 不正なアニメIDが検出されました（行 ${startRow + i}）: '${animeId}'。この行はスキップします。`);
        // 不正なIDの場合もエラーとしてカウントするか、無視するかは要件次第
        // results[userName].errors++;
      }
    }
  });

  console.log("\n--- 全てのユーザーの処理が完了しました ---");
  return { success: overallSuccess, message: "処理が完了しました。", details: results };

}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * DATEシートのC, D, E列の全行が 'watched' または '=SPARKLINE({1,0})' の場合、
 * 2025シートのA列の最大値に1を加えて最終行に記入し、
 * その後DATEシートの1行目以外をクリアする関数。
 */
function updateTestSheetMaxId() {
  Logger.log("[updateTestSheetMaxId] 処理を開始します。");
  const dateSheet = SPREADSHEET.getSheetByName(ID_COPY_TARGET_SHEET_NAME); // DATEシートを取得
  const testSheet = SPREADSHEET.getSheetByName('2025'); // 2025シートを取得

  if (!dateSheet) {
    Logger.log(`[updateTestSheetMaxId] DATEシートが見つかりませんでした。処理をスキップします。`);
    return;
  }
  if (!testSheet) {
    Logger.log(`[updateTestSheetMaxId] 2025シートが見つかりませんでした。処理をスキップします。`);
    return;
  }

  const lastRowDate = dateSheet.getLastRow();
  const startRowDate = 2; // DATEシートのデータはA2から始まる想定

  // DATEシートに有効なIDが存在するかどうかをチェック
  if (lastRowDate < startRowDate) {
    Logger.log("[updateTestSheetMaxId] DATEシートにデータがありません。2025シートの更新をスキップします。");
    return;
  }

  const idRange = dateSheet.getRange(startRowDate, 2, lastRowDate - startRowDate + 1, 1); // B列 (2) からIDを取得
  const idValues = idRange.getValues();
  let hasValidIds = false;
  for (let i = 0; i < idValues.length; i++) {
    const id = String(idValues[i][0]).trim();
    if (id !== '' && !isNaN(id)) {
      hasValidIds = true;
      break;
    }
  }

  if (!hasValidIds) {
    Logger.log("[updateTestSheetMaxId] DATEシートに有効なIDがありません。2025シートの更新をスキップします。");
    // 有効なIDが一つもない場合も、クリアは不要なのでここで終了
    return;
  }

  // DATEシートのC列、D列、E列のデータ（値と数式）を一括で取得
  // getRange(行, 列, 高さ, 幅)
  const statusRange = dateSheet.getRange(startRowDate, 3, lastRowDate - startRowDate + 1, 3); // C2からE列の最終行まで (3列分)
  const statusValues = statusRange.getValues();   // 表示されている値を取得
  const statusFormulas = statusRange.getFormulas(); // 数式を取得

  let allCompleted = true; // 全てのステータスが 'watched' またはスパークラインであるかのフラグ
  const SPARKLINE_FORMULA = '=SPARKLINE({1,0})'; // スパークラインの数式を定数化

  // 各行をチェックし、C, D, E列のいずれかが 'watched' またはスパークライン以外であればフラグをfalseにする
  for (let i = 0; i < statusValues.length; i++) {
    const rowValues = statusValues[i];   // [C列の値, D列の値, E列の値]
    const rowFormulas = statusFormulas[i]; // [C列の数式, D列の数式, E列の数式]

    const cValue = String(rowValues[0] || '').toLowerCase(); // C列の値
    const dValue = String(rowValues[1] || '').toLowerCase(); // D列の値
    const eValue = String(rowValues[2] || '').toLowerCase(); // E列の値

    const cFormula = String(rowFormulas[0] || '').toUpperCase(); // C列の数式（大文字に変換）
    const dFormula = String(rowFormulas[1] || '').toUpperCase(); // D列の数式（大文字に変換）
    const eFormula = String(rowFormulas[2] || '').toUpperCase(); // E列の数式（大文字に変換）

    // 各セルの完了条件をチェック
    const isCCompleted = (cValue === 'watched' || cFormula === SPARKLINE_FORMULA);
    const isDCompleted = (dValue === 'watched' || dFormula === SPARKLINE_FORMULA);
    const isECompleted = (eValue === 'watched' || eFormula === SPARKLINE_FORMULA);

    // いずれか一つでも条件を満たさないセルがあれば、全て完了ではない
    if (!isCCompleted || !isDCompleted || !isECompleted) {
      allCompleted = false;
      Logger.log(`[updateTestSheetMaxId] C, D, E列のいずれかに'watched'またはスパークライン以外のステータスが見つかりました。(行 ${startRowDate + i}): C:${cValue} (${cFormula}), D:${dValue} (${dFormula}), E:${eValue} (${eFormula})`);
      break; // 1つでも条件を満たさない行があればループを抜ける
    }
  }

  if (allCompleted) {
    Logger.log("[updateTestSheetMaxId] DATEシートのC, D, E列の全ての行が 'watched' またはスパークラインです。2025シートを更新します。");

    // 2025シートのA列の最大値を取得
    const testLastRow = testSheet.getLastRow();
    let maxId = 0;
    if (testLastRow >= 3) { // ヘッダー行を考慮
      const testAColumnRange = testSheet.getRange(3, 1, testLastRow - 2, 1);
      const testAColumnValues = testAColumnRange.getValues();

      for (let i = 0; i < testAColumnValues.length; i++) {
        const idValue = testAColumnValues[i][0];
        if (typeof idValue === 'number' && !isNaN(idValue) && idValue > maxId) {
          maxId = idValue;
        }
      }
    }

    const newId = maxId + 1;
    testSheet.getRange(testLastRow + 1, 1).setValue(newId); // 2025シートのA列の最終行の次に新しいIDを記入
    Logger.log(`[updateTestSheetMaxId] 2025シートのA列に新しいID '${newId}' を記入しました。`);

    // DATEシートの1行目以外をクリア
    dateSheet.getRange(startRowDate, 1, lastRowDate - startRowDate + 1, dateSheet.getLastColumn()).clearContent();
    Logger.log(`[updateTestSheetMaxId] DATEシートの2行目から最終行までをクリアしました。`);

    // ★★★ Python APIへのJSON送信処理を追加 ★★★
    sendDataToPython({
      type: "status_update_complete",
      message: "そして次の曲が始まるのです",
      timestamp: new Date().toISOString()
    });
    Logger.log("[updateTestSheetMaxId] Python APIに「そして次の曲が始まるのです」を送信しました。");
    // ★★★ ここまで追加 ★★★

  } else {
    Logger.log("[updateTestSheetMaxId] DATEシートのC, D, E列の全てが 'watched' またはスパークラインではありません。2025シートは更新されません。");
  }
  SpreadsheetApp.flush(); // 保留中の変更を強制的に適用
  Logger.log("[updateTestSheetMaxId] 処理を終了します。");
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/
/**
 * Python APIへデータを送信するヘルパー関数
 * @param {object} payload 送信するJSONデータ
 */
function sendDataToPython(payload) {
  const options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log('Python API Response Code: ' + response.getResponseCode());
    Logger.log('Python API Response Body: ' + response.getContentText());
  } catch (e) {
    Logger.log('Error sending data to Python API: ' + e.toString());
  }
}

/**
 * sendDataToPythonAndDiscord 関数は既存のまま残します。
 * 今後、特定のメッセージをDiscordに送る場合に利用できます。
 */
function sendDataToPythonAndDiscord() {

  // 送信したいメッセージの内容
  const messageToSend = "GASから送信された動的なメッセージです！";
  const statusInfo = "処理が正常に完了しました。";

  // 送信するデータをJSON形式で定義
  const payload = {
    "type": "discord_message",
    "message": messageToSend,
    "status": statusInfo,
    "timestamp": new Date().toISOString() // 現在時刻を追加
  };

  const options = {
    'method': 'post',
    'contentType': 'application/json', // JSONデータを送ることを明示
    'payload': JSON.stringify(payload), // JSONオブジェクトを文字列に変換
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log('Response Code: ' + response.getResponseCode());
    Logger.log('Response Body: ' + response.getContentText());
  } catch (e) {
    Logger.log('Error sending request: ' + e.toString());
  }
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/

/**
 * DATEシートのC列、D列、E列からスパークラインの数式をクリアします。
 * スパークラインが記入されているセルのみを対象とします。
 */
function clearSparklinesFromDateSheet() {
  const dateSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('DATE');
  if (!dateSheet) {
    Logger.log("エラー: 「DATE」という名前のシートが見つかりませんでした。スパークラインのクリアをスキップします。");
    return;
  }

  const startRow = 2; // データ開始行
  const lastRow = dateSheet.getLastRow();
  const SPARKLINE_CHECK_FORMULA = '=SPARKLINE({1,0})'.toUpperCase(); // 比較用に大文字化

  if (lastRow < startRow) {
    Logger.log("DATEシートにクリアするスパークラインのデータがありません。");
    return;
  }

  // C列、D列、E列の数式を一括で取得
  const rangeToScan = dateSheet.getRange(startRow, 3, lastRow - startRow + 1, 3); // C列からE列まで
  const formulasToScan = rangeToScan.getFormulas(); // 数式を取得

  const cellsToClear = []; // クリアするセルを格納する配列

  for (let i = 0; i < formulasToScan.length; i++) {
    const rowFormulas = formulasToScan[i];
    for (let j = 0; j < rowFormulas.length; j++) {
      const cellFormula = String(rowFormulas[j]).toUpperCase();
      // スパークラインの数式が一致する場合
      if (cellFormula === SPARKLINE_CHECK_FORMULA) {
        // スパークラインが見つかったセルの行と列（シート上の絶対位置）を記録
        const actualRow = startRow + i;
        const actualCol = 3 + j; // C列が3、D列が4、E列が5
        cellsToClear.push(dateSheet.getRange(actualRow, actualCol));
      }
    }
  }

  if (cellsToClear.length > 0) {
    // 取得したすべてのセルを一括でクリア
    for (const range of cellsToClear) {
      range.clearContent();
    }
    SpreadsheetApp.flush(); // 保留中の変更を適用
    Logger.log(`${cellsToClear.length} 個のスパークラインをDATEシートからクリアしました。`);
  } else {
    Logger.log("DATEシートにクリア対象のスパークラインは見つかりませんでした。");
  }
}

/**-----------------------------------------------------------------------------------------------------------------------------------------------------------------**/