use miette::Diagnostic;
use tracing::info;

use super::initialize_state;
use crate::{
    app_config, config,
    integrations::{
        ApiRequestError, CreateReleaseInput, CreateReleaseResponse, git, handle_response,
    },
    state,
    state::RunType,
};

pub(crate) fn create_release(
    name: &str,
    tag_name: &str,
    body: &str,
    prerelease: bool,
    gitea_state: RunType<state::Gitea>,
    gitea_config: &config::Gitea,
) -> Result<state::Gitea, Error> {
    let target_commitish = git::get_head_commit_sha().ok();
    let gitea_release = CreateReleaseInput::new(
        tag_name,
        name,
        body,
        prerelease,
        false,
        target_commitish.as_deref(),
    );

    let gitea_state = match gitea_state {
        RunType::DryRun(state) => {
            gitea_release_dry_run(name, gitea_config, &gitea_release);
            return Ok(state);
        }
        RunType::Real(gitea_state) => gitea_state,
    };

    let (token, agent) = initialize_state(&gitea_config.host, gitea_state)?;

    let resp = agent
        .post(&gitea_config.get_releases_url())
        .query("access_token", &token)
        .send_json(gitea_release);
    let resp = handle_response(
        resp,
        gitea_config.host.clone(),
        "creating a release".to_string(),
    )?;
    resp.into_body()
        .read_json::<CreateReleaseResponse>()
        .map_err(|source| Error::ApiResponse {
            source,
            activity: "creating a release",
            host: gitea_config.host.clone(),
        })?;

    Ok(state::Gitea::Initialized { token, agent })
}

fn gitea_release_dry_run(name: &str, config: &config::Gitea, gitea_release: &CreateReleaseInput) {
    let release_type = if gitea_release.prerelease {
        "prerelease"
    } else {
        "release"
    };
    let body = gitea_release.body.as_ref().map_or_else(
        || String::from("autogenerated body"),
        |body| format!("body:\n{body}"),
    );
    let target = gitea_release
        .target_commitish
        .map_or_else(String::new, |sha| format!(" at commit {sha}"));
    info!(
        "Would create a {release_type} on Gitea [{host}] with name {name} and tag {tag}{target} and {body}",
        tag = gitea_release.tag_name,
        host = config.host
    );
}

#[derive(Debug, Diagnostic, thiserror::Error)]
pub(crate) enum Error {
    #[error(transparent)]
    #[diagnostic(transparent)]
    AppConfig(#[from] app_config::Error),
    #[error(transparent)]
    #[diagnostic(transparent)]
    ApiRequest(#[from] ApiRequestError),
    #[error("Trouble decoding the response from Gitea while {activity}: {source}")]
    #[diagnostic(
        code(gitea::api_response_error),
        help(
            "Failure to decode a response from the Gitea instance at {host} is probably a bug. Please report it at https://github.com/knope-dev/knope"
        )
    )]
    ApiResponse {
        source: ureq::Error,
        activity: &'static str,
        host: String,
    },
}
