use indieweb::http::Client;
use indieweb::standards::indieauth::AccessToken;
use indieweb::standards::micropub::action::{Action, ActionResponse, UpdateOperation, OperationValue};
use serde::Serialize;
use url::Url;

#[derive(Serialize)]
pub struct UpdateResult {
    pub url: String,
    pub updated: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub changes: Option<serde_json::Value>,
}

pub async fn run(
    client: &impl Client,
    endpoint: &Url,
    access_token: &AccessToken,
    post_url: &Url,
    replace: Option<Vec<String>>,
    add: Option<Vec<String>>,
    delete_prop: Option<Vec<String>>,
) -> miette::Result<UpdateResult> {
    let operation = if let Some(props) = replace {
        let values: Vec<String> = props.iter().map(|p| {
            let parts: Vec<&str> = p.splitn(2, '=').collect();
            parts.get(1).unwrap_or(&"").to_string()
        }).collect();
        (UpdateOperation::Replace, OperationValue::WholeProperties(values))
    } else if let Some(props) = add {
        (UpdateOperation::Add, OperationValue::WholeProperties(props))
    } else if let Some(props) = delete_prop {
        (UpdateOperation::Delete, OperationValue::WholeProperties(props))
    } else {
        return Err(miette::miette!("No update operation specified"));
    };

    let action = Action::Update {
        url: post_url.clone(),
        operation,
    };

    let response = action.send(client, endpoint, access_token).await?;

    match response {
        ActionResponse::Updated { changes, location } => {
            Ok(UpdateResult {
                url: post_url.to_string(),
                updated: true,
                location: location.map(|u| u.to_string()),
                changes,
            })
        }
        _ => Err(miette::miette!("Unexpected response from Micropub server")),
    }
}
