use crate::storage::Error;
use mongodb::bson::oid::ObjectId;
use mongodb::bson::Bson;
use pbbson::Model;
use std::str::FromStr;

pub(crate) fn from_object_id(id: ObjectId) -> String {
    xid::Id::from_bytes(&id.bytes()).unwrap().to_string()
}

pub(crate) fn try_to_object_id(id: &str) -> Result<ObjectId, Error> {
    const WHAT: &str = "IDs";
    require_xid_base32hex(WHAT, id)?;
    let as_xid = match xid::Id::from_str(id) {
        Ok(xid) => xid,
        Err(e) => {
            let msg = format!("{WHAT} must be 12-byte XID's in 20-byte base32hex encoded form ({e:?})");
            return Err(Error::invalid_argument(msg));
        }
    };
    Ok(ObjectId::from_bytes(*as_xid.as_bytes()))
}

pub(crate) fn require_xid_base32hex(what: &str, id: &str) -> Result<(), Error> {
    if id.len() != 20 {
        return Err(Error::invalid_argument(format!(
            "{what} must be 12-byte XID's in 20-byte base32hex encoded form"
        )));
    }
    Ok(())
}

pub(crate) fn transform_from_mongo(model: Model) -> Result<Model, Error> {
    let mut model = model.clone();
    if let Some(Bson::ObjectId(object_id)) = model.get("_id") {
        let id = xid::Id::from_bytes(&object_id.bytes()).map_err(|e| Error::internal(e.to_string()))?;
        model.insert("id".to_string(), id.to_string());
        model.remove("_id");
    }
    Ok(model)
}
