(function () {
  if (window.GIAComposePanelSend) {
    return;
  }

  const core = window.GIAComposePanelCore;
  if (!core) {
    return;
  }

  const createController = function (config) {
    const panel = config.panel;
    const panelId = config.panelId;
    const state = config.state;
    const thread = config.thread;
    const form = config.form;
    const textarea = config.textarea;
    const statusBox = config.statusBox;
    const manualConfirm = config.manualConfirm;
    const armInput = config.armInput;
    const confirmInput = config.confirmInput;
    const sendButton = config.sendButton;
    const sendCapable = config.sendCapable;
    const csrfToken = config.csrfToken;
    const queryParams = config.queryParams;
    const poll = config.poll;
    const clearReplyTarget = config.clearReplyTarget;
    const autosize = config.autosize;
    const flashCompose = config.flashCompose;
    const setStatus = config.setStatus;

    let transientCancelButton = null;
    let persistentCancelWrap = null;

    const postFormJson = async function (url, params) {
      const response = await fetch(url, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "X-CSRFToken": csrfToken,
          "Content-Type": "application/x-www-form-urlencoded",
          Accept: "application/json",
        },
        body: params.toString(),
      });
      if (!response.ok) {
        throw new Error("Request failed");
      }
      return response.json();
    };

    const cancelSendRequest = function (commandId) {
      return postFormJson(
        String(panel.dataset.cancelSendUrl || ""),
        queryParams({ command_id: String(commandId || "") })
      );
    };

    const hideTransientCancelButton = function () {
      if (!transientCancelButton) {
        return;
      }
      transientCancelButton.remove();
      transientCancelButton = null;
    };

    const showTransientCancelButton = function () {
      if (!statusBox || transientCancelButton) {
        return;
      }
      transientCancelButton = core.createNode(
        "button",
        "button is-danger is-light is-small compose-cancel-send-btn",
        "Cancel Send"
      );
      transientCancelButton.type = "button";
      transientCancelButton.addEventListener("click", async function () {
        try {
          await cancelSendRequest("");
        } catch (_err) {
          // Ignore cancel failures.
        } finally {
          hideTransientCancelButton();
        }
      });
      statusBox.appendChild(transientCancelButton);
    };

    const hidePersistentCancelButton = function () {
      if (!persistentCancelWrap) {
        return;
      }
      persistentCancelWrap.remove();
      persistentCancelWrap = null;
    };

    const stopPendingCommandPolling = function () {
      if (state.pendingCommandPoll) {
        clearInterval(state.pendingCommandPoll);
        state.pendingCommandPoll = null;
      }
      state.pendingCommandId = "";
      state.pendingCommandAttempts = 0;
      state.pendingCommandStartedAt = 0;
      state.pendingCommandInFlight = false;
    };

    const showPersistentCancelButton = function (commandId) {
      hidePersistentCancelButton();
      if (!statusBox) {
        return;
      }
      persistentCancelWrap = core.createNode("div", "compose-persistent-cancel");
      const button = core.createNode(
        "button",
        "button is-danger is-light is-small compose-persistent-cancel-btn",
        "Cancel Queued Send"
      );
      button.type = "button";
      button.addEventListener("click", async function () {
        try {
          await cancelSendRequest(commandId);
          stopPendingCommandPolling();
          hidePersistentCancelButton();
          setStatus("Send cancelled.", "warning");
          await poll(true);
        } catch (_err) {
          hidePersistentCancelButton();
        }
      });
      persistentCancelWrap.appendChild(button);
      statusBox.appendChild(persistentCancelWrap);
    };

    const pollPendingCommandResult = async function (commandId) {
      const url = new URL(
        String(panel.dataset.commandResultUrl || ""),
        window.location.origin
      );
      url.searchParams.set("service", thread.dataset.service || "");
      url.searchParams.set("command_id", commandId);
      url.searchParams.set("format", "json");
      const response = await fetch(url.toString(), {
        credentials: "same-origin",
        headers: { "HX-Request": "true" },
      });
      if (!response.ok || response.status === 204) {
        return null;
      }
      return response.json();
    };

    const startPendingCommandPolling = function (commandId) {
      if (!commandId) {
        return;
      }
      stopPendingCommandPolling();
      state.pendingCommandId = commandId;
      state.pendingCommandStartedAt = Date.now();
      showPersistentCancelButton(commandId);
      state.pendingCommandPoll = setInterval(async function () {
        if (state.pendingCommandInFlight) {
          return;
        }
        state.pendingCommandAttempts += 1;
        if (
          state.pendingCommandAttempts > 14
          || (Date.now() - state.pendingCommandStartedAt) > 45000
        ) {
          stopPendingCommandPolling();
          hidePersistentCancelButton();
          setStatus(
            "Send timed out waiting for runtime result. Please retry.",
            "warning"
          );
          return;
        }
        try {
          state.pendingCommandInFlight = true;
          const payload = await pollPendingCommandResult(commandId);
          if (!payload || payload.pending !== false) {
            return;
          }
          const result = payload.result || {};
          stopPendingCommandPolling();
          hidePersistentCancelButton();
          if (result.ok) {
            setStatus("", "success");
            textarea.value = "";
            clearReplyTarget();
            autosize();
            flashCompose("is-send-success");
            await poll(true);
            return;
          }
          setStatus(String(result.error || "Send failed."), "danger");
          flashCompose("is-send-fail");
          await poll(true);
        } catch (_err) {
          // Ignore transient failures; the next poll can recover.
        } finally {
          state.pendingCommandInFlight = false;
        }
      }, 3500);
    };

    const updateManualSafety = function () {
      const confirmed = !!(manualConfirm && manualConfirm.checked);
      if (armInput) {
        armInput.value = confirmed ? "1" : "0";
      }
      if (confirmInput) {
        confirmInput.value = confirmed ? "1" : "0";
      }
      if (sendButton) {
        sendButton.disabled = !sendCapable || !confirmed;
      }
    };

    const bindSendEvents = function () {
      textarea.addEventListener("keydown", function (event) {
        if (event.key !== "Enter" || event.shiftKey) {
          return;
        }
        event.preventDefault();
        if (sendButton && sendButton.disabled) {
          setStatus("Enable send confirmation before sending.", "warning");
          return;
        }
        form.requestSubmit();
      });

      form.addEventListener("submit", function () {
        if (sendButton && sendButton.disabled) {
          return;
        }
        showTransientCancelButton();
      });

      form.addEventListener("htmx:afterRequest", function () {
        hideTransientCancelButton();
        textarea.focus();
      });
    };

    const bindDocumentEvents = function () {
      state.eventHandler = function (event) {
        const detail = (event && event.detail) || {};
        const sourcePanelId = String(detail.panel_id || "");
        if (sourcePanelId && sourcePanelId !== panelId) {
          return;
        }
        poll(true);
      };
      document.body.addEventListener("composeMessageSent", state.eventHandler);

      state.sendResultHandler = function (event) {
        const detail = (event && event.detail) || {};
        const sourcePanelId = String(detail.panel_id || "");
        if (sourcePanelId && sourcePanelId !== panelId) {
          return;
        }
        hideTransientCancelButton();
        if (detail.ok) {
          flashCompose("is-send-success");
          textarea.value = "";
          clearReplyTarget();
          autosize();
          poll(true);
        } else {
          flashCompose("is-send-fail");
          if (detail.message) {
            setStatus(detail.message, detail.level || "danger");
          }
        }
        textarea.focus();
      };
      document.body.addEventListener("composeSendResult", state.sendResultHandler);

      state.commandIdHandler = function (event) {
        const detail = (event && event.detail) || {};
        const commandId = String(
          detail.command_id
          || (detail.composeSendCommandId && detail.composeSendCommandId.command_id)
          || ""
        ).trim();
        if (commandId) {
          startPendingCommandPolling(commandId);
        }
      };
      document.body.addEventListener(
        "composeSendCommandId",
        state.commandIdHandler
      );
    };

    const init = function () {
      bindSendEvents();
      bindDocumentEvents();
      if (manualConfirm) {
        manualConfirm.addEventListener("change", updateManualSafety);
        manualConfirm.dispatchEvent(new Event("change"));
      } else {
        updateManualSafety();
      }
    };

    return {
      init: init,
      resetForContextSwitch: function () {
        stopPendingCommandPolling();
        hidePersistentCancelButton();
        hideTransientCancelButton();
      },
    };
  };

  window.GIAComposePanelSend = {
    createController: createController,
  };
})();
