(() => {
  document.querySelectorAll("[data-auto-submit='true']").forEach((element) => {
    element.addEventListener("change", () => {
      if (element.form) element.form.submit();
    });
  });
})();

(() => {
  const usage = document.getElementById("Usage");
  const redirect = document.getElementById("RedirectUris");
  const clientType = document.getElementById("ClientType");
  if (!usage || !redirect || !clientType) return;

  const syncRedirectInputState = () => {
    const usageValue = usage.value;
    const needsRedirect = usageValue === "web_login" || usageValue === "webhook_outbound";
    const requiresConfidential = usageValue === "tenant_api"
      || usageValue === "send_api"
      || usageValue === "platform_service"
      || usageValue === "file_api";

    redirect.disabled = !needsRedirect;
    if (!needsRedirect) redirect.value = "";

    const publicOption = clientType.querySelector('option[value="public"]');
    if (publicOption) publicOption.disabled = requiresConfidential;
    if (requiresConfidential) clientType.value = "confidential";
  };

  usage.addEventListener("change", syncRedirectInputState);
  syncRedirectInputState();
})();

(() => {
  const forms = document.querySelectorAll("[data-avatar-upload]");
  if (!forms.length) return;

  const targetSize = 256;
  const maxOutputBytes = 512 * 1024;

  const readFileAsImage = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const image = new Image();
      image.onload = () => resolve(image);
      image.onerror = () => reject(new Error("Invalid image."));
      image.src = reader.result;
    };
    reader.onerror = () => reject(new Error("Invalid image."));
    reader.readAsDataURL(file);
  });

  const canvasToBlob = (canvas, type, quality) => new Promise((resolve) => {
    canvas.toBlob(resolve, type, quality);
  });

  const blobToDataUrl = (blob) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });

  const submitForm = (form) => {
    HTMLFormElement.prototype.submit.call(form);
  };

  forms.forEach((form) => {
    const fileInput = form.querySelector("[data-avatar-file]");
    const dataUrlInput = form.querySelector("[data-avatar-data-url]");
    const contentTypeInput = form.querySelector("[data-avatar-content-type]");
    const widthInput = form.querySelector("[data-avatar-width]");
    const heightInput = form.querySelector("[data-avatar-height]");
    if (!fileInput || !dataUrlInput || !contentTypeInput || !widthInput || !heightInput) return;

    fileInput.addEventListener("change", async () => {
      const file = fileInput.files && fileInput.files[0];
      if (!file) return;

      try {
        const image = await readFileAsImage(file);
        openCropDialog(form, image, () => {
          fileInput.value = "";
        }, async (cropState) => {
          const canvas = document.createElement("canvas");
          canvas.width = targetSize;
          canvas.height = targetSize;

          const context = canvas.getContext("2d");
          if (!context) throw new Error("Canvas is not available.");

          context.drawImage(
            image,
            (targetSize - cropState.renderWidth) / 2 + cropState.offsetX,
            (targetSize - cropState.renderHeight) / 2 + cropState.offsetY,
            cropState.renderWidth,
            cropState.renderHeight);

          let blob = await canvasToBlob(canvas, "image/webp", 0.82);
          if (!blob) blob = await canvasToBlob(canvas, "image/png");
          if (!blob || blob.size > maxOutputBytes) {
            throw new Error("Image output is too large.");
          }

          dataUrlInput.value = await blobToDataUrl(blob);
          contentTypeInput.value = blob.type || "image/png";
          widthInput.value = String(targetSize);
          heightInput.value = String(targetSize);
          submitForm(form);
        });
      } catch {
        fileInput.value = "";
      }
    });
  });

  function openCropDialog(form, image, onCancel, onSave) {
    const strings = {
      title: form.dataset.cropTitle || "Adjust profile image",
      zoom: form.dataset.cropZoom || "Zoom",
      save: form.dataset.cropSave || "Save",
      cancel: form.dataset.cropCancel || "Cancel",
      error: form.dataset.cropError || "Unable to process this image."
    };

    const dialog = document.createElement("div");
    dialog.className = "avatar-crop-modal";
    dialog.innerHTML = `
      <div class="avatar-crop-backdrop" data-avatar-crop-cancel></div>
      <div class="avatar-crop-panel" role="dialog" aria-modal="true" aria-labelledby="avatar-crop-title">
        <div class="avatar-crop-heading">
          <h2 id="avatar-crop-title"></h2>
          <button type="button" class="avatar-crop-close" data-avatar-crop-cancel aria-label="${strings.cancel}">×</button>
        </div>
        <div class="avatar-crop-stage">
          <img alt="" draggable="false" />
        </div>
        <label class="avatar-crop-slider">
          <span></span>
          <input type="range" min="1" max="3" step="0.01" value="1" />
        </label>
        <p class="avatar-crop-error" hidden></p>
        <div class="avatar-crop-actions">
          <button type="button" class="profile-pill-button" data-avatar-crop-cancel></button>
          <button type="button" class="profile-pill-button" data-avatar-crop-save></button>
        </div>
      </div>`;

    dialog.querySelector("#avatar-crop-title").textContent = strings.title;
    dialog.querySelector(".avatar-crop-slider span").textContent = strings.zoom;
    dialog.querySelector("[data-avatar-crop-cancel].profile-pill-button").textContent = strings.cancel;
    dialog.querySelector("[data-avatar-crop-save]").textContent = strings.save;

    const preview = dialog.querySelector(".avatar-crop-stage img");
    const stage = dialog.querySelector(".avatar-crop-stage");
    const zoomInput = dialog.querySelector(".avatar-crop-slider input");
    const error = dialog.querySelector(".avatar-crop-error");

    const state = {
      zoom: 1,
      offsetX: 0,
      offsetY: 0,
      renderWidth: targetSize,
      renderHeight: targetSize
    };

    const baseScale = targetSize / Math.min(image.naturalWidth, image.naturalHeight);
    let dragStart = null;

    const clamp = () => {
      state.renderWidth = image.naturalWidth * baseScale * state.zoom;
      state.renderHeight = image.naturalHeight * baseScale * state.zoom;
      const maxX = Math.max(0, (state.renderWidth - targetSize) / 2);
      const maxY = Math.max(0, (state.renderHeight - targetSize) / 2);
      state.offsetX = Math.max(-maxX, Math.min(maxX, state.offsetX));
      state.offsetY = Math.max(-maxY, Math.min(maxY, state.offsetY));
    };

    const render = () => {
      clamp();
      preview.style.width = `${state.renderWidth}px`;
      preview.style.height = `${state.renderHeight}px`;
      preview.style.transform = `translate(-50%, -50%) translate(${state.offsetX}px, ${state.offsetY}px)`;
    };

    const close = () => {
      dialog.remove();
    };

    preview.src = image.src;
    document.body.appendChild(dialog);
    render();

    dialog.querySelectorAll("[data-avatar-crop-cancel]").forEach((button) => {
      button.addEventListener("click", () => {
        close();
        onCancel();
      });
    });

    zoomInput.addEventListener("input", () => {
      const previousZoom = state.zoom;
      state.zoom = Number(zoomInput.value);
      if (previousZoom > 0) {
        state.offsetX *= state.zoom / previousZoom;
        state.offsetY *= state.zoom / previousZoom;
      }
      render();
    });

    stage.addEventListener("pointerdown", (event) => {
      stage.setPointerCapture(event.pointerId);
      dragStart = {
        x: event.clientX,
        y: event.clientY,
        offsetX: state.offsetX,
        offsetY: state.offsetY
      };
    });

    stage.addEventListener("pointermove", (event) => {
      if (!dragStart) return;
      state.offsetX = dragStart.offsetX + event.clientX - dragStart.x;
      state.offsetY = dragStart.offsetY + event.clientY - dragStart.y;
      render();
    });

    stage.addEventListener("pointerup", () => {
      dragStart = null;
    });

    stage.addEventListener("pointercancel", () => {
      dragStart = null;
    });

    dialog.querySelector("[data-avatar-crop-save]").addEventListener("click", async () => {
      error.hidden = true;
      try {
        await onSave({ ...state });
      } catch {
        error.textContent = strings.error;
        error.hidden = false;
      }
    });
  }
})();
