use anyhow::Result;

use crate::api::{PlaneApiClient, WorkItemIdentifier};
use crate::config::resolve_plane_session;
use super::work_item_fields::{
    WorkItemFieldArgs, WorkItemFieldArgsRaw, WorkItemFieldArgsTarget,
    WorkItemFieldArgsTargetParams,
};

/// Batch-updates one or more fields on an existing work item in a single API call.
/// Supports changing state, priority, labels, assignee, and description.
/// The work item identifier (e.g. "NRO-123") is required as the first argument.
#[derive(clap::Args)]
pub struct UpdateWorkItemArgs {
    /// Work item identifier to update (e.g. "NRO-123")
    identifier: String,
    /// New state name (matched case-insensitively against project states)
    #[arg(long)]
    state: Option<String>,
    /// New priority level: urgent, high, medium, low, none
    #[arg(long)]
    priority: Option<String>,
    /// Label name to attach (repeatable: --label "bug" --label "frontend")
    #[arg(long)]
    label: Vec<String>,
    /// Module name to assign (matched case-insensitively against project modules)
    #[arg(long)]
    module: Vec<String>,
    /// Parent work item identifier (e.g. --parent NRO-45)
    #[arg(long)]
    parent: Vec<String>,
    /// Start date in YYYY-MM-DD format
    #[arg(long = "start-date")]
    start_date: Vec<String>,
    /// Due date in YYYY-MM-DD format
    #[arg(long)]
    due: Vec<String>,
    /// Plain text description (wrapped in paragraph tags)
    #[arg(long)]
    description: Vec<String>,
    /// Estimate points (0-7)
    #[arg(long)]
    estimate: Vec<String>,
    /// Assignee UUID (repeatable: --assignee UUID --assignee UUID)
    #[arg(long)]
    assignee: Vec<String>,
    /// Preview changes without sending API requests
    #[arg(long)]
    dry_run: Option<String>,
}

/// Execution logic for the `plane update` subcommand
impl UpdateWorkItemArgs {
    /// Applies the specified field changes to the work item via the Plane API
    /// and confirms each updated field in the output
    pub async fn execute(self) -> Result<()> {
        let plane_session = resolve_plane_session()?;
        let api_client = PlaneApiClient::try_new(plane_session)?;

        let work_item_identifier: WorkItemIdentifier = self.identifier.parse()?;
        let fetched_work_item = api_client
            .find_work_item_by_identifier(&work_item_identifier)
            .await?;

        let mut request_fields = serde_json::Map::new();

        if let Option::Some(priority_text) = self.priority {
            request_fields.insert(
                String::from("priority"),
                serde_json::Value::String(priority_text.to_lowercase()),
            );
        }

        if let Option::Some(state_name) = self.state {
            let project_states = api_client
                .list_states(fetched_work_item.project_id())
                .await?;
            let mut matched_state_id = Option::None;
            for candidate_state in &project_states {
                if candidate_state
                    .display_name()
                    .eq_ignore_ascii_case(&state_name)
                {
                    matched_state_id = Option::Some(candidate_state.id().as_str().to_owned());
                }
            }
            match matched_state_id {
                Option::Some(state_uuid) => {
                    request_fields.insert(
                        String::from("state"),
                        serde_json::Value::String(state_uuid),
                    );
                }
                Option::None => {
                    let available: Vec<&str> =
                        project_states.iter().map(|s| s.display_name()).collect();
                    anyhow::bail!(
                        "state '{}' not found — available: {}",
                        state_name,
                        available.join(", ")
                    );
                }
            }
        }

        let shared_fields = WorkItemFieldArgs::from_raw_args(WorkItemFieldArgsRaw {
            labels: self.label,
            module: self.module,
            parent: self.parent,
            start_date: self.start_date,
            due: self.due,
            description: self.description,
            estimate: self.estimate,
            assignees: self.assignee,
        })?;
        let apply_target = WorkItemFieldArgsTarget::new(WorkItemFieldArgsTargetParams {
            request_fields: &mut request_fields,
            api_client: &api_client,
            project_id: fetched_work_item.project_id(),
        });
        shared_fields.apply_to(apply_target).await?;

        if request_fields.is_empty() {
            anyhow::bail!(
                "no fields to update — pass --state, --priority, or other flags"
            );
        }

        let request_body_json = serde_json::Value::Object(request_fields).to_string();

        match self.dry_run {
            Option::Some(_) => {
                println!(
                    "Dry run: {} — would apply: {}",
                    work_item_identifier, request_body_json
                );
            }
            Option::None => {
                let project_client = api_client.project_operations(fetched_work_item.project_id());
                let item_client = project_client.work_item_operations(fetched_work_item.id());
                let updated_item = item_client.update_fields(&request_body_json).await?;

                println!(
                    "Updated {}: {}",
                    work_item_identifier,
                    updated_item.title().as_str()
                );
            }
        }

        Ok(())
    }
}
