use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TreeShellDragEffect {
    Copy,
    Move,
    None,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TreeShellDragPayload {
    pub row_ids: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TreeShellDropTarget {
    pub row_id: Option<String>,
    pub document_id: Option<String>,
    pub asset_id: Option<String>,
}

pub fn resolve_drag_effect(has_external_files: bool, alt_key: bool) -> TreeShellDragEffect {
    if has_external_files || alt_key {
        TreeShellDragEffect::Copy
    } else {
        TreeShellDragEffect::Move
    }
}

pub fn normalize_drag_payload(row_ids: &[String]) -> Option<TreeShellDragPayload> {
    let mut normalized = Vec::new();
    for row_id in row_ids {
        let row_id = row_id.trim();
        if row_id.is_empty() || normalized.iter().any(|existing| existing == row_id) {
            continue;
        }
        normalized.push(row_id.to_string());
    }
    if normalized.is_empty() {
        None
    } else {
        Some(TreeShellDragPayload {
            row_ids: normalized,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{normalize_drag_payload, resolve_drag_effect, TreeShellDragEffect};

    fn ids(values: &[&str]) -> Vec<String> {
        values.iter().map(|value| (*value).to_string()).collect()
    }

    #[test]
    fn tree_shell_drag_drop_state_normalizes_payload_and_effect() {
        let payload = normalize_drag_payload(&ids(&[" doc:a ", "asset:b", "doc:a", ""])).unwrap();
        assert_eq!(payload.row_ids, ids(&["doc:a", "asset:b"]));
        assert_eq!(resolve_drag_effect(true, false), TreeShellDragEffect::Copy);
        assert_eq!(resolve_drag_effect(false, true), TreeShellDragEffect::Copy);
        assert_eq!(resolve_drag_effect(false, false), TreeShellDragEffect::Move);
        assert!(normalize_drag_payload(&ids(&["", " "])).is_none());
    }
}
