/// Open a playlist file and stage it for review. Parsing and resolution only —
/// nothing is written until `playlist_import_commit`.
///
/// The phone has no dialog here, so it picks the file itself (the storage
/// plugin's document picker) and hands the path in — with the picked
/// document's own `name` beside it, because a document that had no path of
/// its own arrives as a copy in the app's cache, and the NAME is what
/// `detect_format` reads the extension off and what titles the draft. The
/// desktop passes neither and opens its own dialog.
#[tauri::command]
pub async fn playlist_import_probe(
    app: tauri::AppHandle,
    lib: State<'_, LibraryState>,
    path: Option<String>,
    name: Option<String>,
) -> Result<Option<ImportDraft>, String> {
    let picked = match path {
        Some(p) if !p.trim().is_empty() => Some(p),
        _ => {
            let (tx, rx) = tokio::sync::oneshot::channel::<Option<String>>();
            #[cfg(desktop)]
            {
                use tauri_plugin_dialog::DialogExt;
                app.dialog()
                    .file()
                    .add_filter(
                        "Playlist",
                        &["m3u", "m3u8", "pls", "xspf", "csv", "json", "txt"],
                    )
                    .pick_file(move |path| {
                        let _ = tx.send(path.map(|p| p.to_string()));
                    });
            }
            #[cfg(mobile)]
            {
                let _ = &app;
                let _ = tx.send(None);
            }
            rx.await.unwrap_or(None)
        }
    };
    let Some(source) = picked else {
        return Ok(None);
    };
    let source_path = PathBuf::from(&source);
    // The picked name wins over the path's own: a cache copy is named after
    // the document, but only the document's name is guaranteed to be it.
    let name = match name {
        Some(n) if !n.trim().is_empty() => n,
        _ => source_path
            .file_name()
            .map(|n| n.to_string_lossy().into_owned())
            .unwrap_or_else(|| source.clone()),
    };
