interface WritableFileStream {
  write(data: Blob): Promise<void>;
  close(): Promise<void>;
}

interface SaveFileHandle {
  createWritable(): Promise<WritableFileStream>;
}

interface SavePickerOptions {
  suggestedName?: string;
  types?: Array<{
    description?: string;
    accept: Record<string, string[]>;
  }>;
}

type SaveFilePicker = (options?: SavePickerOptions) => Promise<SaveFileHandle>;

function getSaveFilePicker(): SaveFilePicker | undefined {
  const value = (
    globalThis as typeof globalThis & {
      showSaveFilePicker?: SaveFilePicker;
    }
  ).showSaveFilePicker;
  return typeof value === 'function' ? value.bind(globalThis) : undefined;
}

export async function saveBlob(
  blob: Blob,
  fileName: string,
  mimeType = blob.type || 'application/octet-stream'
): Promise<'saved' | 'downloaded' | 'cancelled'> {
  const picker = getSaveFilePicker();
  if (picker) {
    try {
      const extension = fileName.includes('.')
        ? `.${fileName.split('.').at(-1)}`
        : '';
      const handle = await picker({
        suggestedName: fileName,
        types: [
          {
            description: 'Media output',
            accept: { [mimeType]: extension ? [extension] : [] },
          },
        ],
      });
      const writable = await handle.createWritable();
      await writable.write(blob);
      await writable.close();
      return 'saved';
    } catch (error) {
      if (error instanceof DOMException && error.name === 'AbortError') {
        return 'cancelled';
      }
      throw error;
    }
  }

  downloadBlob(blob, fileName);
  return 'downloaded';
}

export function downloadBlob(blob: Blob, fileName: string): void {
  const temporaryUrl = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = temporaryUrl;
  link.download = fileName;
  link.rel = 'noopener';
  link.click();
  setTimeout(() => URL.revokeObjectURL(temporaryUrl), 1_000);
}
