use anyhow::{Result, bail};
use chrono::Local;
use dialoguer::{MultiSelect, Select, theme::ColorfulTheme};
use crate::db::jira_inbox::JiraInboxItem;
use crate::db::tags::Tag;
use crate::libs::pause::Pause;
use crate::libs::prompt::ensure_interactive;
use crate::libs::task::Task;
fn select(prompt: &str, labels: &[String]) -> Result<usize> {
Ok(Select::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.items(labels)
.default(0)
.interact()?)
}
pub fn inbox_issue(items: &[JiraInboxItem], prompt: &str) -> Result<String> {
if items.is_empty() {
bail!("the inbox is empty - run `kasl inbox sync` to fetch assigned issues");
}
ensure_interactive("issue key is required; pass KEY outside a terminal")?;
let now = Local::now().naive_local();
let width = items.iter().map(|i| i.issue_key.len()).max().unwrap_or(0);
let labels: Vec<String> = items
.iter()
.map(|item| {
let pin = if item.pinned { "*" } else { " " };
let badge = item.badge(now).map(|b| format!(" [{b}]")).unwrap_or_default();
format!("{pin}{:width$} {}{badge}", item.issue_key, item.summary)
})
.collect();
Ok(items[select(prompt, &labels)?].issue_key.clone())
}
pub fn tasks(tasks: &[Task], prompt: &str) -> Result<Vec<i32>> {
if tasks.is_empty() {
bail!("no tasks to choose from - `kasl task add` creates one");
}
ensure_interactive("task id is required; pass ID outside a terminal")?;
let labels: Vec<String> = tasks
.iter()
.map(|t| format!("[{}] {} ({}%)", t.id.unwrap_or(0), t.name, t.completeness.unwrap_or(0)))
.collect();
let chosen = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.items(&labels)
.interact()?;
Ok(chosen.into_iter().filter_map(|i| tasks[i].id).collect())
}
pub fn tag(tags: &[Tag], prompt: &str) -> Result<String> {
if tags.is_empty() {
bail!("no tags yet - run `kasl tag add` first");
}
ensure_interactive("tag name is required; pass TAG outside a terminal")?;
let width = tags.iter().map(|t| t.name.len()).max().unwrap_or(0);
let labels: Vec<String> = tags
.iter()
.map(|t| match &t.color {
Some(color) if !color.is_empty() => format!("{:width$} {color}", t.name),
_ => t.name.clone(),
})
.collect();
Ok(tags[select(prompt, &labels)?].name.clone())
}
pub fn pause(pauses: &[Pause], prompt: &str) -> Result<i32> {
if pauses.is_empty() {
bail!("no pauses recorded for that day - `kasl pauses list` shows what is there");
}
ensure_interactive("pause id is required; pass ID outside a terminal")?;
let labels: Vec<String> = pauses
.iter()
.map(|p| {
let start = p.start.format("%H:%M");
let end = p.end.map(|e| e.format("%H:%M").to_string()).unwrap_or_else(|| "…".to_string());
match p.duration {
Some(d) => format!("{start} - {end} ({} min)", d.num_minutes()),
None => format!("{start} - {end} (ongoing)"),
}
})
.collect();
Ok(pauses[select(prompt, &labels)?].id)
}