use std::process::Command;

use anyhow::{Context, Result};

use crate::api::{PlaneApiClient, StateGroup, WorkItemIdentifier};
use crate::config::{ProjectSlug, resolve_plane_session};

/// Creates a git branch from a work item identifier and transitions the item to In Progress.
/// The branch name follows the pattern `feat/<ID>-<slugified-title>` (e.g. `feat/NRO-123-add-auth`).
/// Usage: `plane start NRO-123`
#[derive(clap::Args)]
pub struct StartWorkItemArgs {
    /// Work item identifier to start working on (e.g. "NRO-123")
    identifier: String,
}

/// Execution logic for the `plane start` subcommand
impl StartWorkItemArgs {
    /// Resolves the work item, creates a feature branch, checks it out,
    /// and transitions the work item to the first "started" state in the project
    pub async fn execute(self) -> Result<()> {
        let parsed_identifier: WorkItemIdentifier = self.identifier.parse()?;

        let plane_session = resolve_plane_session()?;
        let api_client = PlaneApiClient::try_new(plane_session)?;

        let fetched_work_item = api_client
            .find_work_item_by_identifier(&parsed_identifier)
            .await?;

        let branch_builder = BranchNameBuilder {
            work_item_identifier: &parsed_identifier,
        };
        let branch_name = branch_builder.build_from_title(fetched_work_item.title().as_str());

        let branch_creator = GitBranchCreator { branch_name: &branch_name };
        branch_creator.create_and_checkout()?;

        let project_slug_text = parsed_identifier.project_identifier().to_lowercase();
        let project_slug: ProjectSlug = project_slug_text.parse()?;
        let resolved_project = api_client.find_project_by_slug(&project_slug).await?;

        let project_states = api_client.list_states(resolved_project.id()).await?;

        let mut started_state_id = Option::None;
        for candidate_state in &project_states {
            match candidate_state.state_group() {
                StateGroup::Started => {
                    started_state_id = Option::Some(candidate_state.id().as_str().to_owned());
                    break;
                }
                _ => {}
            }
        }

        let resolved_started_state_id = match started_state_id {
            Option::Some(state_uuid) => state_uuid,
            Option::None => {
                println!("Checked out branch: {}", branch_name);
                println!("Warning: no 'started' state found in project — state not transitioned");
                return Ok(());
            }
        };

        let mut request_body = serde_json::Map::new();
        request_body.insert(
            String::from("state"),
            serde_json::Value::String(resolved_started_state_id),
        );
        let request_body_json = serde_json::to_string(&request_body)?;

        let scoped_client = api_client.project_operations(resolved_project.id());
        let work_item_client = scoped_client.work_item_operations(fetched_work_item.id());
        work_item_client.update_fields(&request_body_json).await?;

        println!("Checked out branch: {}", branch_name);
        println!("Transitioned {} to In Progress", parsed_identifier);

        Ok(())
    }
}

/// Constructs git branch names from work item identifiers and titles.
/// Produces branches like `feat/NRO-123-add-auth-middleware` with slugified titles.
struct BranchNameBuilder<'a> {
    work_item_identifier: &'a WorkItemIdentifier,
}

/// Branch name construction from work item title text
impl<'a> BranchNameBuilder<'a> {
    fn build_from_title(&self, title_text: &str) -> String {
        let mut slug_chars = String::with_capacity(64);

        for title_char in title_text.chars() {
            match title_char.is_ascii_alphanumeric() {
                true => slug_chars.push(title_char.to_ascii_lowercase()),
                false => {
                    if !slug_chars.ends_with('-') {
                        slug_chars.push('-');
                    }
                }
            }
        }

        let trimmed_slug = slug_chars.trim_end_matches('-');

        let max_slug_length: usize = 40;
        let truncated_slug = match trimmed_slug.len() > max_slug_length {
            true => {
                let safe_boundary = match trimmed_slug.get(..max_slug_length) {
                    Option::Some(prefix) => match prefix.rfind('-') {
                        Option::Some(hyphen_position) => hyphen_position,
                        Option::None => max_slug_length,
                    },
                    Option::None => max_slug_length,
                };
                match trimmed_slug.get(..safe_boundary) {
                    Option::Some(bounded_prefix) => bounded_prefix,
                    Option::None => trimmed_slug,
                }
            }
            false => trimmed_slug,
        };

        let mut branch_name = String::with_capacity(64);
        branch_name.push_str("feat/");
        branch_name.push_str(&self.work_item_identifier.to_string());
        branch_name.push('-');
        branch_name.push_str(truncated_slug);
        branch_name
    }
}

/// Encapsulates git operations for creating and checking out a feature branch
struct GitBranchCreator<'a> {
    branch_name: &'a str,
}

/// Git branch creation and checkout via subprocess
impl<'a> GitBranchCreator<'a> {
    fn create_and_checkout(&self) -> Result<()> {
        let checkout_output = Command::new("git")
            .args(["checkout", "-b", self.branch_name])
            .output()
            .context("failed to run git checkout")?;

        if !checkout_output.status.success() {
            let stderr_text = String::from_utf8_lossy(&checkout_output.stderr);
            anyhow::bail!("git checkout -b failed: {}", stderr_text.trim());
        }

        Ok(())
    }
}
