use indieweb::http::Client;
use indieweb::standards::webmention::{Request, Status};
use indieweb_cli_common::Config;
use serde::Serialize;
use url::Url;

#[derive(Serialize)]
pub struct SendResult {
    pub source: String,
    pub target: String,
    pub status: String,
    pub location: Option<String>,
    pub body: Option<String>,
}

pub async fn run(
    client: &impl Client,
    config: &Config,
    source: &Url,
    target: &Url,
    endpoint: Option<&Url>,
    token: Option<&str>,
) -> miette::Result<SendResult> {
    let endpoint_url = if let Some(ep) = endpoint {
        ep.clone()
    } else if let Some(ep) = &config.webmention.endpoint {
        ep.clone()
    } else {
        indieweb::standards::webmention::endpoint_for(client, target).await?
    };

    let request = Request {
        source: source.clone(),
        target: target.clone(),
        private: None,
        vouch: vec![],
        token: token.map(ToString::to_string),
    };

    request.validate()?;

    let response = indieweb::standards::webmention::send(client, &endpoint_url, &request).await?;

    Ok(SendResult {
        source: source.to_string(),
        target: target.to_string(),
        status: status_to_string(&response.status),
        location: response.location.map(|u| u.to_string()),
        body: response.body,
    })
}

fn status_to_string(status: &Status) -> String {
    match status {
        Status::Sent => "sent".to_string(),
        Status::Accepted => "accepted".to_string(),
        Status::Rejected => "rejected".to_string(),
        Status::SenderError(code) => format!("sender_error_{}", code),
        Status::ReceiverError(code) => format!("receiver_error_{}", code),
    }
}
