/**
 * Centralized file saving with native "Save As" dialog support.
 *
 * Uses the File System Access API (showSaveFilePicker) when available
 * so users can choose the filename and location. Falls back to a normal
 * download anchor in browsers that don't support it (e.g. Firefox, Safari).
 */

type SaveSource = Blob | string | ArrayBuffer | Uint8Array;

interface SaveOptions {
  /** Suggested filename (with extension). */
  suggestedName: string;
  /** MIME type, e.g. "application/pdf", "text/csv". */
  mimeType?: string;
  /** Friendly description for the picker, e.g. "PDF Document". */
  description?: string;
}

const EXT_TO_MIME: Record<string, string> = {
  pdf: "application/pdf",
  csv: "text/csv",
  txt: "text/plain",
  json: "application/json",
  xml: "application/xml",
  html: "text/html",
  htm: "text/html",
  png: "image/png",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  webp: "image/webp",
  gif: "image/gif",
  svg: "image/svg+xml",
  zip: "application/zip",
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  ics: "text/calendar",
};

function inferMime(name: string, fallback?: string): string {
  if (fallback) return fallback;
  const ext = name.split(".").pop()?.toLowerCase() ?? "";
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
}

function toBlob(source: SaveSource, mime: string): Blob {
  if (source instanceof Blob) return source;
  if (typeof source === "string") return new Blob([source], { type: mime });
  return new Blob([source as BlobPart], { type: mime });
}

function legacyDownload(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  a.rel = "noopener";
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  setTimeout(() => {
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }, 0);
}

/**
 * Save a file. Prompts the user with the native "Save As" dialog when supported.
 * Returns true if saved, false if the user cancelled.
 */
export async function saveFile(source: SaveSource, opts: SaveOptions): Promise<boolean> {
  const name = opts.suggestedName || "download";
  const mime = inferMime(name, opts.mimeType);
  const blob = toBlob(source, mime);

  // Modern path: native "Save As" dialog
  const anyWindow = window as any;
  if (typeof anyWindow.showSaveFilePicker === "function") {
    try {
      const ext = name.split(".").pop()?.toLowerCase();
      const accept: Record<string, string[]> = {};
      if (ext) accept[mime] = [`.${ext}`];

      const handle = await anyWindow.showSaveFilePicker({
        suggestedName: name,
        types: ext
          ? [{ description: opts.description || `${ext.toUpperCase()} file`, accept }]
          : undefined,
      });
      const writable = await handle.createWritable();
      await writable.write(blob);
      await writable.close();
      return true;
    } catch (err: any) {
      // User cancelled the picker — treat as a no-op, do not fall back.
      if (err?.name === "AbortError") return false;
      // Any other error (e.g. SecurityError in iframes) — fall through to legacy.
      console.warn("showSaveFilePicker failed, falling back to download:", err);
    }
  }

  legacyDownload(blob, name);
  return true;
}

/**
 * Save a jsPDF document via the native "Save As" dialog when available.
 * Use this instead of `doc.save(filename)`.
 */
export async function savePdfDoc(doc: { output: (type: "blob") => Blob }, suggestedName: string) {
  const blob = doc.output("blob");
  return saveFile(blob, { suggestedName, mimeType: "application/pdf", description: "PDF Document" });
}

/**
 * Save CSV text content via the native "Save As" dialog when available.
 */
export async function saveCsv(csv: string, suggestedName: string) {
  // Prepend BOM so Excel opens UTF-8 correctly
  const withBom = csv.startsWith("\ufeff") ? csv : "\ufeff" + csv;
  return saveFile(withBom, { suggestedName, mimeType: "text/csv;charset=utf-8;", description: "CSV file" });
}

/**
 * Download a remote URL through the native "Save As" dialog.
 * Falls back to opening the URL in a new tab if CORS blocks the fetch.
 */
export async function saveUrl(url: string, suggestedName: string, mimeType?: string) {
  try {
    const res = await fetch(url, { mode: "cors" });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const blob = await res.blob();
    return saveFile(blob, { suggestedName, mimeType: mimeType || blob.type });
  } catch (err) {
    console.warn("saveUrl: direct fetch failed, opening in new tab:", err);
    window.open(url, "_blank", "noopener,noreferrer");
    return false;
  }
}
