1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use cod_cli::pull_request::edit::EditPullRequestArgs;
use cod_client::CodebergClient;
use cod_endpoints::endpoint_generator::EndpointGenerator;
use cod_render::ui::{fuzzy_select_with_key, multi_fuzzy_select_with_key};
use cod_types::api::edit_options::edit_pull_request_option::EditPullRequestOption;
use cod_types::api::pull_request::PullRequest;
use cod_types::api::state_type::StateType;
use strum::{Display, EnumIter, IntoEnumIterator};

use crate::text_manipulation::{edit_prompt_for, input_prompt_for, select_prompt_for};

#[derive(Display, EnumIter, PartialEq, Eq)]
enum EditableFields {
    Assignees,
    Description,
    State,
    Labels,
    Title,
}

pub async fn edit_pull(_args: EditPullRequestArgs, client: &CodebergClient) -> anyhow::Result<()> {
    let list_pull_requests = client.get_repo_prs(None, None).await?;

    let selected_pull_request =
        fuzzy_select_with_key(list_pull_requests, select_prompt_for("pull request")).and_then(
            |maybe_selected| {
                maybe_selected.ok_or_else(|| anyhow::anyhow!("Nothing selected. Aborting."))
            },
        )?;

    let selected_update_fields = multi_fuzzy_select_with_key(
        EditableFields::iter().collect::<Vec<_>>(),
        select_prompt_for("option"),
        |_| false,
    )?;

    let edit_pull_request_options =
        create_update_data(client, selected_update_fields, &selected_pull_request).await?;

    let api_endpoint = EndpointGenerator::repo_update_pull_request(selected_pull_request.number)?;

    let updated_pull_request: PullRequest = client
        .patch_body(api_endpoint, edit_pull_request_options)
        .await?;

    tracing::debug!("{updated_pull_request:?}");

    println!("Successfully updated pull request");

    Ok(())
}

async fn create_update_data(
    client: &CodebergClient,
    selected_update_fields: Vec<EditableFields>,
    selected_pull_request: &PullRequest,
) -> anyhow::Result<EditPullRequestOption> {
    use EditableFields::*;

    let mut edit_pull_request_options =
        EditPullRequestOption::from_pull_request(selected_pull_request);

    if selected_update_fields.contains(&Assignees) {
        let assignees_list = client.get_repo_assignees().await?;
        let selected_assignees = multi_fuzzy_select_with_key(
            assignees_list,
            select_prompt_for("assignees"),
            |assignee| {
                selected_pull_request
                    .assignees
                    .as_ref()
                    .map_or(false, |assignees| assignees.contains(assignee))
            },
        )?;
        edit_pull_request_options.assignees.replace(
            selected_assignees
                .into_iter()
                .map(|assignee| assignee.username)
                .collect::<Vec<_>>(),
        );
    }

    if selected_update_fields.contains(&Labels) {
        let labels_list = client.get_repo_labels(None).await?;
        let selected_labels =
            multi_fuzzy_select_with_key(labels_list, select_prompt_for("labels"), |label| {
                selected_pull_request.labels.contains(label)
            })?;
        edit_pull_request_options.labels.replace(
            selected_labels
                .into_iter()
                .map(|label| label.id)
                .collect::<Vec<_>>(),
        );
    }

    if selected_update_fields.contains(&Description) {
        let new_description = inquire::Editor::new(edit_prompt_for("a description").as_str())
            .with_predefined_text(selected_pull_request.body.as_str())
            .prompt()?;
        edit_pull_request_options.body.replace(new_description);
    }

    if selected_update_fields.contains(&State) {
        let new_state = fuzzy_select_with_key(
            StateType::available_for_choosing().to_vec(),
            select_prompt_for("state"),
        )?;
        edit_pull_request_options
            .state
            .replace(new_state.unwrap_or(selected_pull_request.state));
    }

    if selected_update_fields.contains(&Title) {
        let new_title = inquire::Text::new(input_prompt_for("Choose a new issue title").as_str())
            .with_default(selected_pull_request.title.as_str())
            .prompt()?;
        edit_pull_request_options.title.replace(new_title);
    }

    Ok(edit_pull_request_options)
}