Skip to content
use anyhow::{bail, Result};
use dialoguer::{Confirm, Editor, FuzzySelect, Input};

use crate::cache::{Cache, Project, StatusOption};
use crate::config::Config;
use crate::github::GitHubClient;

pub struct IssueInput {
    pub repo_full_name: String,
    pub title: String,
    pub description: String,
    pub project: Option<Project>,
    pub status: Option<StatusOption>,
}

pub fn collect_issue_input(config: &Config, cache: &Cache) -> Result<IssueInput> {
    // Sort repos by descending usage count so frequently used ones appear first.
    let mut sorted_repos = cache.repos.clone();
    sorted_repos.sort_by(|a, b| {
        let count_a = cache.usage.repos.get(&a.full_name).copied().unwrap_or(0);
        let count_b = cache.usage.repos.get(&b.full_name).copied().unwrap_or(0);
        count_b.cmp(&count_a)
    });

    let repo_names: Vec<String> = sorted_repos.iter().map(|r| r.full_name.clone()).collect();
    if repo_names.is_empty() {
        bail!("No repositories found in cache. Run `pike setup` first.");
    }

    let repo_idx = FuzzySelect::new()
        .with_prompt("Select repository")
        .items(&repo_names)
        .interact()?;
    let repo_full_name = repo_names[repo_idx].clone();

    let default_title = format!("unknown-{}", rand::random::<u16>());
    let title: String = Input::new()
        .with_prompt("Issue title")
        .default(default_title)
        .interact()?;

    let description = if Confirm::new()
        .with_prompt("Add a description?")
        .default(false)
        .interact()?
    {
        Editor::new()
            .edit("")
            .ok()
            .flatten()
            .map(|t| t.trim().to_string())
            .unwrap_or_default()
    } else {
        String::new()
    };

    let project = select_project(config, cache)?;

    let status = if let Some(ref proj) = project {
        select_status(proj)?
    } else {
        None
    };

    Ok(IssueInput {
        repo_full_name,
        title,
        description,
        project,
        status,
    })
}

fn select_project(config: &Config, cache: &Cache) -> Result<Option<Project>> {
    if cache.projects.is_empty() {
        return Ok(None);
    }

    // Sort projects by descending usage count so frequently used ones appear first.
    let mut sorted_projects = cache.projects.clone();
    sorted_projects.sort_by(|a, b| {
        let count_a = cache.usage.projects.get(&a.id).copied().unwrap_or(0);
        let count_b = cache.usage.projects.get(&b.id).copied().unwrap_or(0);
        count_b.cmp(&count_a)
    });

    let project_items: Vec<String> = sorted_projects.iter().map(|p| p.title.clone()).collect();
    let mut options = vec!["Skip (no project)".to_string()];
    options.extend(project_items.clone());

    let default_idx = config
        .default_project
        .as_ref()
        .and_then(|dp| project_items.iter().position(|p| p == dp))
        .map(|i| i + 1)
        .unwrap_or(0);

    let idx = FuzzySelect::new()
        .with_prompt("Select project")
        .items(&options)
        .default(default_idx)
        .interact()?;

    if idx == 0 {
        Ok(None)
    } else {
        Ok(Some(sorted_projects[idx - 1].clone()))
    }
}

fn select_status(project: &Project) -> Result<Option<StatusOption>> {
    if project.status_options.is_empty() {
        return Ok(None);
    }

    let mut options = vec!["No status".to_string()];
    options.extend(project.status_options.iter().map(|s| s.name.clone()));

    let idx = FuzzySelect::new()
        .with_prompt("Select status")
        .items(&options)
        .default(0)
        .interact()?;

    if idx == 0 {
        Ok(None)
    } else {
        Ok(Some(project.status_options[idx - 1].clone()))
    }
}

pub async fn run_setup(token: &str) -> Result<()> {
    println!("  GITHUB_TOKEN found");

    let default_org: String = Input::new()
        .with_prompt("Default organization")
        .interact()?;

    let config = Config {
        default_org: Some(default_org.clone()),
        default_project: None,
        favorite_repos: Vec::new(),
    };
    config.save()?;
    println!("  Config saved");

    let client = GitHubClient::new(token);

    println!("Fetching repositories...");
    let repos = client.list_org_repos(&default_org).await?;
    println!("  Fetched {} repositories", repos.len());

    println!("Fetching projects...");
    let projects = client.list_org_projects(&default_org).await?;
    println!("  Fetched {} projects", projects.len());

    let cache = Cache { repos, projects, ..Cache::default() };
    cache.save()?;
    println!("  Cache saved");

    Ok(())
}