import { assetUrl } from "../../../api";
import { isInAppBrowser } from "../../../utils/inAppBrowser";

export const IN_APP_DOWNLOAD_GUIDE_EVENT = "ark:in-app-download-guide";

export type InAppDownloadGuideDetail = { url: string; filename: string };

/**
 * Files larger than this skip the fetch→Blob path so mobile WebViews and
 * memory-constrained devices do not OOM. They fall back to the anchor
 * download (relies on the backend's `Content-Disposition: attachment`).
 */
const MAX_BLOB_DOWNLOAD_BYTES = 50 * 1024 * 1024;

export type DownloadOptions = { sizeBytes?: number };

export function pauseActiveVideos() {
  document.querySelectorAll("video").forEach((video) => {
    video.pause();
  });
}

export function attachmentDownloadUrl(postId: string, attachmentId: string) {
  return assetUrl(
    `/api/posts/${encodeURIComponent(postId)}/attachments/${encodeURIComponent(
      attachmentId,
    )}/download`,
  );
}

export async function downloadAttachment(
  postId: string,
  attachmentId: string,
  filename: string,
  options?: DownloadOptions,
) {
  return downloadFile(
    attachmentDownloadUrl(postId, attachmentId),
    filename,
    options,
  );
}

export async function downloadFile(
  url: string,
  filename: string,
  options?: DownloadOptions,
) {
  const safeFilename = filename || "download";

  // In-app WebViews (WeChat / TokenPocket / Telegram / iOS WKWebView / …)
  // ignore `Content-Disposition: attachment` and have no system download
  // manager, so an anchor click would just open the file inline. Surface an
  // "open in external browser" guide instead of silently failing the user.
  if (typeof window !== "undefined" && isInAppBrowser()) {
    window.dispatchEvent(
      new CustomEvent<InAppDownloadGuideDetail>(IN_APP_DOWNLOAD_GUIDE_EVENT, {
        detail: { url: toAbsoluteUrl(url), filename: safeFilename },
      }),
    );
    return;
  }

  // Normal browsers: prefer fetch → Blob → object URL so the file always lands
  // in the Downloads folder with the original filename, even when the browser
  // would otherwise inline-preview the response (Chrome and Safari do this for
  // PDFs / images regardless of Content-Disposition). Fall back to the anchor
  // download for large files (avoid loading them entirely into memory) or when
  // fetch fails for any reason.
  const sizeBytes = options?.sizeBytes ?? 0;
  if (sizeBytes <= MAX_BLOB_DOWNLOAD_BYTES) {
    try {
      const res = await fetch(url, { credentials: "omit" });
      if (res.ok) {
        const blob = await res.blob();
        const objectUrl = URL.createObjectURL(blob);
        try {
          triggerDownload(objectUrl, safeFilename);
        } finally {
          // Give the browser a moment to start the download before revoking.
          window.setTimeout(() => URL.revokeObjectURL(objectUrl), 4000);
        }
        return;
      }
    } catch {
      // fall through to anchor fallback
    }
  }

  triggerDownload(url, safeFilename);
}

function toAbsoluteUrl(url: string): string {
  if (/^https?:\/\//i.test(url)) return url;
  if (typeof window === "undefined") return url;
  try {
    return new URL(url, window.location.origin).toString();
  } catch {
    return url;
  }
}

function triggerDownload(url: string, filename: string) {
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  a.style.display = "none";
  document.body.append(a);
  a.click();
  a.remove();
}
