use crate::exporter::{export_ncl, ExportRequest};
use crate::manifest::{self, Manifest};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tracing::{debug, error, info};

#[derive(Debug, Serialize, Deserialize)]
pub struct SyncRequest {
    pub path: String,
    /// Import paths to use for this file's re-export. If empty, daemon falls back to defaults.
    pub import_paths: Vec<String>,
}

/// Drain sidecars from `cache_dir`, then route each request to the correct cache
/// (global or workspace) based on the file's path.
///
/// Sidecar files `.sync-<pid>.json` may be written by Nu processes in either cache.
/// The daemon is called repeatedly with both dirs during its event loop.
pub async fn drain_and_process(
    sidecar_dir: &Path,
    workspace: &Path,
    default_import_paths: &[String],
) -> Result<()> {
    let mut entries = tokio::fs::read_dir(sidecar_dir).await?;
    let mut requests: Vec<SyncRequest> = Vec::new();
    let mut claimed: Vec<PathBuf> = Vec::new();

    while let Some(entry) = entries.next_entry().await? {
        let name = entry.file_name();
        let name = name.to_string_lossy();
        if name.starts_with(".sync-") && name.ends_with(".json") {
            let orig = entry.path();
            let processing = sidecar_dir.join(format!(".processing-{}", &name[6..]));
            if tokio::fs::rename(&orig, &processing).await.is_ok() {
                claimed.push(processing);
            }
        }
    }

    for path in &claimed {
        match tokio::fs::read_to_string(path).await {
            Ok(content) => match serde_json::from_str::<Vec<SyncRequest>>(&content) {
                Ok(reqs) => requests.extend(reqs),
                Err(e) => debug!("parse sync-request {}: {}", path.display(), e),
            },
            Err(e) => debug!("read sync-request {}: {}", path.display(), e),
        }
        tokio::fs::remove_file(path).await.ok();
    }

    if requests.is_empty() {
        return Ok(());
    }

    info!("processing {} sync request(s)", requests.len());

    for req in requests {
        let import_paths = if req.import_paths.is_empty() {
            default_import_paths.to_vec()
        } else {
            req.import_paths
        };
        let file = PathBuf::from(&req.path);

        // Route per-file: extensions → global cache, workspace files → workspace cache.
        let target_cache = manifest::resolve_cache_dir_for_file(&file, Some(workspace));

        let mut mani = match Manifest::load(&target_cache) {
            Ok(m) => m,
            Err(e) => {
                error!("load manifest for {}: {}", target_cache.display(), e);
                continue;
            }
        };

        let export_req = ExportRequest { path: file, import_paths };
        if let Err(e) = export_ncl(&export_req, &target_cache, &mut mani).await {
            error!("sync-request export {}: {}", req.path, e);
            continue;
        }

        if let Err(e) = mani.save(&target_cache) {
            error!("save manifest after sync export: {}", e);
        }
    }

    Ok(())
}
