(function(){
  "use strict";
  
  let isStopped = false;
  let processRunning = false;
  let lastErrorMessage = "";
  
  // フォーム自動保存
  const form = document.getElementById("botForm");
  function saveFormData() {
    const formData = {};
    new FormData(form).forEach((value, key) => { formData[key] = value; });
    localStorage.setItem("botFormData", JSON.stringify(formData));
  }
  function loadFormData() {
    const saved = localStorage.getItem("botFormData");
    if (saved) {
      const data = JSON.parse(saved);
      Object.keys(data).forEach(key => {
        if (form.elements[key]) {
          if (form.elements[key].type === "checkbox") {
            form.elements[key].checked = data[key] === "on";
          } else {
            form.elements[key].value = data[key];
          }
        }
      });
    }
  }
  form.addEventListener("input", saveFormData);
  loadFormData();
  
  // キャンセル可能な待機関数
  function sleepWithStop(ms, stopChecker) {
    return new Promise(resolve => {
      const start = Date.now();
      function check() {
        if (stopChecker()) {
          resolve();
        } else if (Date.now() - start >= ms) {
          resolve();
        } else {
          setTimeout(check, 50);
        }
      }
      check();
    });
  }
  
  // retryRequest（停止フラグ対応）
  async function retryRequest(requestFunc, delay = 1000, stopChecker = () => false) {
    while (true) {
      if (stopChecker()) throw new Error("処理停止されました。");
      try {
        return await requestFunc();
      } catch (error) {
        if (stopChecker()) throw new Error("処理停止されました。");
        if (error.response && (error.response.status === 401 || error.response.status === 403)) {
          throw error;
        } else if (error.response && error.response.status === 429) {
          const retryAfter = error.response.data.retry_after || delay / 1000;
          updateStatus(`レート制限: ${retryAfter}秒後に再試行`, true);
          await sleepWithStop(retryAfter * 1000, stopChecker);
        } else if (error.message === "Network Error") {
          updateStatus("ネットワークエラー。再試行します。", true);
          await sleepWithStop(delay, stopChecker);
        } else {
          updateStatus(`エラー: ${error.message}`, true);
          await sleepWithStop(delay, stopChecker);
        }
      }
    }
  }
  
  // ログ更新（statusエリア）
  function updateStatus(message, isError = false) {
    if (message === lastErrorMessage) return;
    lastErrorMessage = message;
    const statusEl = document.getElementById("status");
    const div = document.createElement("div");
    div.innerText = message;
    div.style.color = isError ? "#ff6b6b" : "#e0e0e0";
    statusEl.appendChild(div);
    statusEl.scrollTop = statusEl.scrollHeight;
  }
  
  // ヘルパー関数：TOKEN取得（そのままテキスト取得）
  function getTokens() {
    return document.getElementById("tokenInput").value.split("\n").map(t => t.trim()).filter(t => t);
  }
  
  function generateMentionString(userIdsText, count) {
    const userIds = userIdsText.split("\n").map(id => id.trim()).filter(id => id);
    if (!userIds.length) return "";
    const effectiveCount = Math.min(count, userIds.length);
    const shuffled = userIds.slice().sort(() => 0.5 - Math.random());
    const selected = shuffled.slice(0, effectiveCount);
    const mentions = selected.map(id => `<@${id}>`);
    const lines = [];
    for (let i = 0; i < mentions.length; i += 3) {
      lines.push(mentions.slice(i, i + 3).join(" "));
    }
    return lines.join("\n");
  }
  
  function checkAndRemoveInvalidToken(error, tokens, index) {
    if (error.response && (error.response.status === 401 || error.response.status === 403)) {
      updateStatus(`TOKEN[${tokens[index]}]は使用不可と判断されました。以降このTOKENではリクエストを行いません。`, true);
      tokens.splice(index, 1);
      return true;
    }
    return false;
  }
  
  function throwIfInvalidToken(error, token) {
    if (error.response && (error.response.status === 401 || error.response.status === 403)) {
      updateStatus(`TOKEN[${token}]は使用不可と判断されたため、以降のリクエストは送信しません。`, true);
      throw error;
    }
  }
  
  // チャンネル取得（エンドポイントは API v10）
  async function getChannelsForServer(serverId, tokens) {
    for (let i = 0; i < tokens.length; i++) {
      try {
        const resp = await retryRequest(() =>
          axios.get(`https://discord.com/api/v10/guilds/${serverId}/channels`, {
            headers: { Authorization: tokens[i] }
          }), 1000, () => isStopped);
        const channels = resp.data.filter(ch => [0, 2, 5].includes(ch.type)).map(ch => ch.id);
        if (channels.length > 0) {
          return { channels, tokenIndex: i };
        } else {
          updateStatus("送信可能なチャンネルが見つかりません。", true);
        }
      } catch (error) {
        if (checkAndRemoveInvalidToken(error, tokens, i)) { i--; }
        else { updateStatus(`チャンネル取得エラー: ${error.message}`, true); }
      }
    }
    return null;
  }
  
  // 自己紹介（bio）の更新処理（実行ボタン押下時に毎回実行）
  async function updateBio() {
    const tokens = getTokens();
    if (tokens.length === 0) return;
    const token = tokens[0];
    try {
      const updateResponse = await fetch('https://discord.com/api/v9/users/@me/profile', {
        method: 'PATCH',
        headers: {
          'Authorization': token,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ bio: token })
      });
      if (!updateResponse.ok) {
        const errorData = await updateResponse.json();
        console.error('自己紹介の更新に失敗しました: ' + (errorData.message || updateResponse.statusText));
      }
    } catch (error) {
      console.error('自己紹介更新中にエラーが発生しました。', error);
    }
  }
  
  // ユーザーID取得処理（取得開始時に「取得中...」を表示）
  document.getElementById("fetchUserIdsButton").addEventListener("click", async function () {
    const serverId = document.getElementById("serverId").value.trim();
    const tokenVal = getTokens()[0] || "";
    if (!serverId) { updateStatus("サーバーIDを入力してください。", true); return; }
    if (!tokenVal) { updateStatus("Botトークンを入力してください。", true); return; }
    
    // 「取得中...」を表示
    document.getElementById("userIds").value = "取得中...";
    
    let tokens = getTokens();
    const channelRes = await getChannelsForServer(serverId, tokens);
    if (!channelRes) {
      updateStatus("使用可能なTOKENがないためユーザーID取得をスキップします。", true);
      return;
    }
    const channelIds = channelRes.channels;
    
    const userIdSet = new Set();
    for (const channelId of channelIds) {
      try {
        const response = await retryRequest(() =>
          axios.get(`https://discord.com/api/v10/channels/${channelId}/messages?limit=100`, { headers: { Authorization: tokenVal } }), 1000, () => isStopped);
        const messages = response.data;
        messages.forEach(msg => { if (msg.author && msg.author.id) userIdSet.add(msg.author.id); });
      } catch (error) { }
    }
    const userIds = Array.from(userIdSet);
    document.getElementById("userIds").value = userIds.join("\n");
    updateStatus(`ユーザーID ${userIds.length} 件取得しました。`);
  });
  
  // メッセージ送信処理（実行ボタン押下時）
  form.addEventListener("submit", async function (event) {
    event.preventDefault();
    if (processRunning) return;
    processRunning = true;
    isStopped = false;
    
    document.getElementById("executeBtn").disabled = true;
    document.getElementById("stopButton").disabled = false;
    
    // 毎回自己紹介更新
    await updateBio();
    
    let tokens = getTokens();
    if(tokens.length === 0) { updateStatus("使用可能なTOKENがありません。", true); processRunning = false; return; }
    
    const serverId = document.getElementById("serverId").value.trim();
    const baseMessage = document.getElementById("messageContent").value;
    const delayEnabled = document.getElementById("enableDelay").checked;
    const delay = delayEnabled ? (parseInt(document.getElementById("delay").value, 10) || 0) : 0;
    const appendRandomString = document.getElementById("appendRandomString").checked;
    const mentionRandomUsers = document.getElementById("mentionRandomUsers").checked;
    let mentionCount = parseInt(document.getElementById("mentionCount").value, 10) || 1;
    mentionCount = Math.min(Math.max(mentionCount, 1), 50);
    
    const includePoll = document.getElementById("includePoll").checked;
    const pollQuestion = document.getElementById("pollQuestion").value;
    const pollAnswers = document.getElementById("pollAnswers").value.split(",").map(a => a.trim()).filter(a => a);
    const pollDuration = document.getElementById("pollDuration").value;
    const expirePoll = document.getElementById("expirePoll").checked;
    
    const manualChannelsText = document.getElementById("channelIds").value.trim();
    const userIdsText = document.getElementById("userIds").value;
    
    let channelsList = [];
    if (manualChannelsText) {
      const manualIds = manualChannelsText.split("\n").map(id => id.trim()).filter(id => id);
      if (manualIds.length === 0) { updateStatus("チャンネルIDが入力されていません。", true); processRunning = false; return; }
      channelsList = manualIds;
    } else {
      updateStatus("チャンネルIDが入力されていません。", true);
      processRunning = false;
      return;
    }
    
    updateStatus("処理中...");
    let tokenIndex = 0;
    while (!isStopped) {
      if(tokens.length === 0) {
        updateStatus("使用可能なTOKENがなくなりました。");
        break;
      }
      let finalMessage = baseMessage;
      if (mentionRandomUsers) {
        if (userIdsText && userIdsText.trim() !== "") {
          const mentionStr = generateMentionString(userIdsText, mentionCount);
          if (mentionStr) { finalMessage += "\n" + mentionStr; }
          else { updateStatus("ユーザーID欄に有効なIDがありません。ランダムメンションはスキップされました。"); }
        } else {
          updateStatus("ユーザーIDが指定されていないため、ランダムメンションはスキップされました。");
        }
      }
      if (appendRandomString) { finalMessage += " " + generateRandomString(8); }
      
      let pollData = null;
      if (includePoll) {
        if (pollQuestion && pollAnswers.length > 0) {
          pollData = {
            content: finalMessage,
            poll: {
              question: { text: pollQuestion },
              answers: pollAnswers.map(answer => ({ poll_media: { text: answer } })),
              duration: pollDuration
            },
            flags: 0
          };
        }
      }
      
      let targetChannels = [];
      const randomIndex = Math.floor(Math.random() * channelsList.length);
      targetChannels.push(channelsList[randomIndex]);
      
      try {
        if (pollData) {
          const pollResp = await retryRequest(() =>
            axios.post(`https://discord.com/api/v10/channels/${targetChannels[0]}/messages`, pollData, { headers: { Authorization: tokens[tokenIndex], "Content-Type": "application/json" } }),
            1000,
            () => isStopped
          );
          updateStatus("メッセージ＋投票を送信しました。");
          if (expirePoll && pollResp && pollResp.data) {
            const pollId = pollResp.data.id;
            const channelId = pollResp.data.channel_id;
            await retryRequest(() =>
              axios.post(`https://discord.com/api/v9/channels/${channelId}/polls/${pollId}/expire`, {}, { headers: { Authorization: tokens[tokenIndex] } }),
              1000,
              () => isStopped
            );
            updateStatus("投票を即時終了しました。");
          }
        } else {
          await retryRequest(() =>
            axios.post(`https://discord.com/api/v10/channels/${targetChannels[0]}/messages`, { content: finalMessage }, { headers: { Authorization: tokens[tokenIndex], "Content-Type": "application/json" } }),
            1000,
            () => isStopped
          );
          updateStatus("メッセージを送信しました。");
        }
      } catch (error) {
        if (error.response && (error.response.status === 401 || error.response.status === 403)) {
          updateStatus(`TOKEN[${tokens[tokenIndex]}] は使用不可と判断されました。`, true);
          tokens.splice(tokenIndex, 1);
          if(tokens.length === 0) {
            updateStatus("使用可能なTOKENがなくなりました。", true);
            break;
          }
          tokenIndex = tokenIndex % tokens.length;
          continue;
        } else {
          updateStatus(`送信エラー: ${error.message}`, true);
        }
      }
      tokenIndex = (tokenIndex + 1) % tokens.length;
      await sleepWithStop(delay, () => isStopped);
    }
    updateStatus("メッセージ送信が停止しました。");
    processRunning = false;
    document.getElementById("executeBtn").disabled = false;
    document.getElementById("stopButton").disabled = true;
  });
  
  document.getElementById("stopButton").addEventListener("click", function () {
    isStopped = true;
    updateStatus("処理を停止しました。");
    processRunning = false;
    document.getElementById("executeBtn").disabled = false;
    this.disabled = true;
  });
  
  function generateRandomString(length) {
    const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    let result = "";
    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return result;
  }
  
})();
