// Phase 9 shared helpers. Centralises the save-dialog + write-file dance
// that HistoryPage (and any future consumer that wants to export a
// transcript as Markdown) calls. Keeps three responsibilities in one
// place: filename suggestion, single-file save, and bulk-to-directory
// export.

import { save, open } from "@tauri-apps/plugin-dialog";
import { invoke } from "@tauri-apps/api/core";
import { toasts } from "$lib/stores/toasts.svelte";
import { buildMarkdown } from "$lib/utils/frontmatter";
import { hasTauriRuntime } from "$lib/utils/runtime";
import type { TranscriptEntry } from "$lib/types/app";

/** Build "<slug>-<YYYY-MM-DD>.md" from a transcript entry. Slug keeps
 *  a-z0-9-; everything else collapses to a hyphen. Empty titles fall
 *  back to "transcript". Trailing hyphens and runs collapse. */
export function suggestedFilename(item: TranscriptEntry): string {
  const raw = (item.title || "transcript").toLowerCase();
  const slug =
    raw
      .replace(/[^a-z0-9]+/g, "-")
      .replace(/^-+|-+$/g, "")
      .slice(0, 60) || "transcript";
  const iso = new Date(item.createdAt || (item as { date?: string }).date || Date.now())
    .toISOString()
    .slice(0, 10);
  return `${slug}-${iso}.md`;
}

/** Save a single transcript via the system Save dialog. Resolves to the
 *  chosen path on success, null if the user cancelled. No clipboard
 *  fallback. */
export async function saveTranscriptAsMarkdown(
  item: TranscriptEntry,
): Promise<string | null> {
  if (!hasTauriRuntime()) {
    toasts.error("Export unavailable", "Native save dialogs are only available in the desktop app.");
    return null;
  }
  const md = buildMarkdown(item);
  const defaultPath = suggestedFilename(item);
  let path: string | null = null;
  try {
    path = await save({
      title: "Save transcript as Markdown",
      defaultPath,
      filters: [{ name: "Markdown", extensions: ["md"] }],
    });
  } catch (err) {
    console.error("saveTranscriptAsMarkdown dialog failed", err);
    toasts.error("Couldn't open save dialog", String(err));
    return null;
  }
  if (!path) return null;
  try {
    await invoke("write_text_file_cmd", { path, contents: md });
    toasts.success(`Saved to ${basename(path)}`);
    return path;
  } catch (err) {
    toasts.error("Couldn't save transcript", String(err));
    return null;
  }
}

/** Bulk export N transcripts to a user-chosen directory. One file per
 *  transcript, filename collisions inside the batch suffixed " (2)",
 *  " (3)" etc. Resolves to the count of files actually written.
 *
 *  We do not check for pre-existing files in the chosen directory, so
 *  exporting on top of an already-populated folder can clobber. The
 *  user picked the folder, the OS picker shows them the contents — that
 *  is judged sufficient signalling for a Phase 9 polish item. Adding a
 *  filesystem-check loop is post-v0.1 territory if it actually bites. */
export async function exportTranscriptsToDir(
  items: TranscriptEntry[],
): Promise<number> {
  if (items.length === 0) return 0;
  if (!hasTauriRuntime()) {
    toasts.error("Export unavailable", "Native folder dialogs are only available in the desktop app.");
    return 0;
  }

  let dir: string | string[] | null = null;
  try {
    dir = await open({
      directory: true,
      multiple: false,
      title: "Choose export folder",
    });
  } catch (err) {
    console.error("exportTranscriptsToDir dialog failed", err);
    toasts.error("Couldn't open export folder dialog", String(err));
    return 0;
  }
  if (!dir || typeof dir !== "string") return 0;

  const used = new Set<string>();
  let written = 0;
  for (const item of items) {
    const base = suggestedFilename(item);
    const finalName = uniquify(base, used);
    used.add(finalName.toLowerCase());
    const path = `${dir}${pathSeparator(dir)}${finalName}`;
    try {
      await invoke("write_text_file_cmd", {
        path,
        contents: buildMarkdown(item),
      });
      written += 1;
    } catch (err) {
      console.error("exportTranscriptsToDir write failed", path, err);
    }
  }
  if (written === 0) {
    // All writes failed — surface as an error instead of "Exported 0
    // transcripts" success toast which reads like the user just bulk-
    // exported nothing on purpose.
    toasts.error(
      "Couldn't export transcripts",
      `0 of ${items.length} written to ${basename(dir)}. Check the console for details.`,
    );
  } else if (written === items.length) {
    toasts.success(
      `Exported ${written} transcript${written === 1 ? "" : "s"} to ${basename(dir)}`,
    );
  } else {
    toasts.warn(
      `Partial export to ${basename(dir)}`,
      `${written} of ${items.length} transcripts written. Check the console for the failures.`,
    );
  }
  return written;
}

function uniquify(name: string, taken: Set<string>): string {
  if (!taken.has(name.toLowerCase())) return name;
  const dot = name.lastIndexOf(".");
  const stem = dot > 0 ? name.slice(0, dot) : name;
  const ext = dot > 0 ? name.slice(dot) : "";
  let n = 2;
  while (taken.has(`${stem} (${n})${ext}`.toLowerCase())) n += 1;
  return `${stem} (${n})${ext}`;
}

function basename(p: string): string {
  const sep = p.includes("\\") ? "\\" : "/";
  const parts = p.split(sep);
  return parts[parts.length - 1] || p;
}

function pathSeparator(dir: string): string {
  return dir.includes("\\") ? "\\" : "/";
}
