// GCG export delivery: Web Share (with the file) on mobile where supported — including the iOS
// Telegram Mini App. An Android in-app WebView (Telegram / VK) has NO Web Share and silently ignores
// an <a download>, so there the tiny GCG text is copied to the clipboard instead; a plain desktop
// browser, where the anchor download works, still downloads a Blob. The Capacitor-native file save
// lands with the native wrapper. pickGcgDelivery is the pure decision, unit-tested with a mock
// navigator; the caller supplies the platform-aware clipboard copy.

import type { GcgExport } from './model';

type ShareNav = Pick<Navigator, 'canShare' | 'share'>;

/**
 * pickGcgDelivery decides how to deliver the GCG file. Web Share (with the file) wins wherever it is
 * available — the iOS Telegram Mini App and mobile browsers. Failing that, an in-app WebView (Android
 * Telegram / VK) has no Web Share and silently ignores an <a download>, so the tiny GCG text is copied
 * to the clipboard ('copy'); only a plain desktop browser, where the anchor download works, falls
 * through to 'download'. Pure, so it is unit-tested with a mock navigator.
 */
export function pickGcgDelivery(
  nav: ShareNav | undefined,
  file: File,
  inAppWebView: boolean,
): 'share' | 'copy' | 'download' {
  if (
    nav &&
    typeof nav.canShare === 'function' &&
    typeof nav.share === 'function' &&
    nav.canShare({ files: [file] })
  ) {
    return 'share';
  }
  return inAppWebView ? 'copy' : 'download';
}

/**
 * shareOrDownloadGcg delivers the GCG export by the best available route and reports which it took —
 * 'shared', 'copied', 'downloaded' or 'failed' — so the caller can confirm a silent copy to the user.
 * inAppWebView marks an Android Telegram/VK WebView (no Web Share, a dead <a download>); copyText is
 * the platform-aware clipboard write (VKWebAppCopyText inside VK, navigator.clipboard otherwise).
 */
export async function shareOrDownloadGcg(
  gcg: GcgExport,
  inAppWebView: boolean,
  copyText: (text: string) => Promise<boolean>,
): Promise<'shared' | 'copied' | 'downloaded' | 'failed'> {
  const file = new File([gcg.content], gcg.filename, { type: 'application/x-gcg' });
  const nav = typeof navigator !== 'undefined' ? navigator : undefined;
  const decision = pickGcgDelivery(nav, file, inAppWebView);
  if (decision === 'share' && nav) {
    // Web Share is available (mobile, including the iOS Telegram Mini App): use it and stop here
    // whatever the outcome. Do NOT fall back to the Blob download — on iOS WKWebView an <a download>
    // navigates the webview to the blob: URL, replacing the SPA with the raw file and stranding the
    // app, so a cancelled or failed share must simply do nothing (the user can retry).
    try {
      await nav.share({ files: [file], title: gcg.filename });
    } catch {
      /* cancelled or failed — intentionally a no-op (see above) */
    }
    return 'shared';
  }
  if (decision === 'copy') {
    // Android Telegram/VK: no Web Share and a dead <a download>, so copy the GCG text instead of
    // silently issuing an anchor click that saves nothing.
    return (await copyText(gcg.content)) ? 'copied' : 'failed';
  }
  downloadFile(gcg.content, gcg.filename);
  return 'downloaded';
}

function downloadFile(content: Blob | string, filename: string, type = 'application/x-gcg'): void {
  if (typeof document === 'undefined') return;
  const blob = typeof content === 'string' ? new Blob([content], { type }) : content;
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

/**
 * downloadUrl saves a same-origin signed export URL as a file through a temporary
 * anchor: the server names it via Content-Disposition, the download attribute is the
 * same-origin hint. This is the browser leg of the unified export delivery; the in-app
 * WebViews never reach it (they use the platform's native download call instead), so
 * the iOS blob-URL navigation trap does not apply — this is a real https URL.
 */
export function downloadUrl(url: string, filename: string): void {
  if (typeof document === 'undefined') return;
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
}

/**
 * shareUrlAsFile delivers a signed export URL through the OS share sheet where files can
 * be shared (mobile browsers): it fetches the same-origin bytes, wraps them in a File and
 * calls navigator.share — the sheet opens directly, nothing lands in Downloads first. The
 * fetch-then-share chain keeps the click's user activation (the proven GCG pattern).
 * Where file sharing is absent (desktop) it falls back to the plain anchor download. A
 * cancelled share is a no-op by the shareOrDownloadGcg invariant.
 */
export async function shareUrlAsFile(url: string, filename: string, mime: string): Promise<'shared' | 'downloaded' | 'failed'> {
  const nav = typeof navigator !== 'undefined' ? navigator : undefined;
  if (nav && typeof nav.share === 'function' && typeof nav.canShare === 'function') {
    try {
      const resp = await fetch(url);
      if (!resp.ok) return 'failed';
      const file = new File([await resp.blob()], filename, { type: mime });
      if (nav.canShare({ files: [file] })) {
        try {
          // No title: iOS pastes it as accompanying text next to the image; the named
          // file speaks for itself.
          await nav.share({ files: [file] });
        } catch {
          /* cancelled — intentionally a no-op */
        }
        return 'shared';
      }
    } catch {
      return 'failed';
    }
  }
  downloadUrl(url, filename);
  return 'downloaded';
}

type TextShareNav = Pick<Navigator, 'share'> & { canShare?: Navigator['canShare'] };

/**
 * pickTextShare decides how to deliver a plain-text payload: through the OS share sheet (Web Share,
 * available on mobile including the Telegram Mini App) or, on a desktop browser without it, a
 * clipboard copy. Pure, so it is unit-tested with a mock navigator.
 */
export function pickTextShare(nav: TextShareNav | undefined): 'share' | 'copy' {
  if (nav && typeof nav.share === 'function' && (typeof nav.canShare !== 'function' || nav.canShare({ text: 'x' }))) {
    return 'share';
  }
  return 'copy';
}

/**
 * shareText delivers text through the OS share sheet where supported, else copies it to the
 * clipboard. It reports the path taken — 'shared', 'copied', or 'failed' (a cancelled share or an
 * unavailable clipboard) — so the caller can confirm a silent copy to the user. Like
 * shareOrDownloadGcg it never strands the webview: a cancelled share simply does nothing.
 */
export async function shareText(text: string, title: string): Promise<'shared' | 'copied' | 'failed'> {
  const nav = typeof navigator !== 'undefined' ? navigator : undefined;
  if (!nav) return 'failed';
  if (pickTextShare(nav) === 'share') {
    try {
      await nav.share({ title, text });
      return 'shared';
    } catch {
      return 'failed';
    }
  }
  try {
    await nav.clipboard.writeText(text);
    return 'copied';
  } catch {
    return 'failed';
  }
}
