use crate::git_remote::{GitRemoteHelper, OptionError};
use std::process;

pub async fn parse(
    lines: &mut impl std::iter::Iterator<Item = String>,
    helper: &mut impl GitRemoteHelper,
) {
    while let Some(line) = lines.next() {
        let line = line.trim();

        if line.is_empty() {
            break; // End of input
        }

        // Parse command
        let parts: Vec<&str> = line.split_whitespace().collect();

        match parts[0] {
            "" => {
                // do nothing
            }
            "capabilities" => {
                println!("option");
                println!("*fetch");
                println!("*push");
                println!("");
            }
            "list" => {
                let for_push = parts.get(1).map(|s| *s == "for-push").unwrap_or(false);
                let refs = helper.list(for_push).await;
                for r in &refs {
                    println!("{} {}", r.points_at, r.name);
                }
                println!("");
            }
            "option" => {
                let response = if parts.len() == 3 {
                    helper
                        .option(parts[1].to_string(), parts[2].to_string())
                        .await
                } else {
                    Err(OptionError::Error(
                        "Error: option requires name and value".to_string(),
                    ))
                };
                match response {
                    Ok(()) => println!("ok"),
                    Err(OptionError::Unsupported) => println!("unsupported"),
                    Err(OptionError::Error(str)) => println!("error {str}"),
                }
            }
            "fetch" => {
                // Collect batch of fetch commands
                let mut batch = vec![(parts[1].to_string(), parts[2].to_string())];

                loop {
                    let next_line = match lines.next() {
                        Some(l) => l,
                        None => break,
                    };

                    let next_line = next_line.trim();

                    if next_line.is_empty() {
                        break; // End of batch
                    }

                    let next_parts: Vec<&str> = next_line.split_whitespace().collect();
                    if next_parts.is_empty() {
                        break;
                    }

                    if next_parts[0] == "fetch" && next_parts.len() >= 3 {
                        batch.push((next_parts[1].to_string(), next_parts[2].to_string()));
                    } else {
                        eprintln!(
                            "Error: unexpected command in fetch batch: {}",
                            next_parts[0]
                        );
                        process::exit(1);
                    }
                }

                // Execute all fetches in the batch
                for (sha1, name) in batch {
                    if let Err(e) = helper.fetch(sha1, name).await {
                        eprintln!("Fetch error: {}", e);
                        process::exit(1);
                    }
                }

                // Output blank line to signal completion
                println!();
            }
            "push" => {
                // Parse first push command
                if parts.len() < 2 {
                    eprintln!("Error: push requires src:dst");
                    process::exit(1);
                }
                let refspec_parts: Vec<&str> = parts[1].split(':').collect();
                if refspec_parts.len() != 2 {
                    eprintln!("Error: push refspec must be src:dst");
                    process::exit(1);
                }

                // Collect batch of push commands with their destinations
                let mut batch = vec![(refspec_parts[0].to_string(), refspec_parts[1].to_string())];

                loop {
                    let next_line = match lines.next() {
                        Some(l) => l,
                        None => break,
                    };

                    let next_line = next_line.trim();

                    if next_line.is_empty() {
                        break; // End of batch
                    }

                    let next_parts: Vec<&str> = next_line.split_whitespace().collect();
                    if next_parts.is_empty() {
                        break;
                    }

                    if next_parts[0] == "push" && next_parts.len() >= 2 {
                        let next_refspec_parts: Vec<&str> = next_parts[1].split(':').collect();
                        if next_refspec_parts.len() != 2 {
                            eprintln!("Error: push refspec must be src:dst");
                            process::exit(1);
                        }
                        batch.push((
                            next_refspec_parts[0].to_string(),
                            next_refspec_parts[1].to_string(),
                        ));
                    } else {
                        eprintln!("Error: unexpected command in push batch: {}", next_parts[0]);
                        process::exit(1);
                    }
                }

                // Execute all pushes in the batch and output results
                for (src, dst) in batch {
                    match helper.push(src, dst.clone()).await {
                        Ok(()) => println!("ok {}", dst),
                        Err(e) => println!("error {} {}", dst, e),
                    }
                }

                // Output blank line to signal completion
                println!();
            }
            _ => {
                eprintln!("Error: unknown command '{}'", parts[0]);
                process::exit(1);
            }
        }
    }
}
