use std::path::PathBuf;

use anyhow::Result;
use chrono::{Local, Utc};
use vtcode_core::scheduler::{
    DURABLE_SCHEDULER_RUNTIME_HINT, DurableTaskStore, ScheduleCreateInput, durable_task_is_overdue,
};
use vtcode_core::utils::ansi::MessageStyle;
use vtcode_tui::app::{
    InlineListItem, InlineListSearchConfig, InlineListSelection, WizardModalMode, WizardStep,
};

use crate::agent::runloop::slash_commands::ScheduleCommandAction;
use crate::agent::runloop::unified::wizard_modal::{
    WizardModalOutcome, show_wizard_modal_and_wait,
};

use super::{SlashCommandContext, SlashCommandControl};

const SCHEDULE_ACTION_PREFIX: &str = "schedule.action.";
const SCHEDULE_ACTION_BACK: &str = "schedule.action.back";
const SCHEDULE_TASK_SHOW_PREFIX: &str = "schedule.task.show.";
const SCHEDULE_TASK_DELETE_PREFIX: &str = "schedule.task.delete.";
const SCHEDULE_INPUT_ID: &str = "schedule.input";

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum InteractiveTaskKind {
    Prompt,
    Reminder,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum InteractiveScheduleKind {
    Every,
    Cron,
    At,
}

pub(crate) async fn handle_manage_schedule(
    mut ctx: SlashCommandContext<'_>,
    action: ScheduleCommandAction,
) -> Result<SlashCommandControl> {
    if !super::scheduler_enabled(ctx.vt_cfg.as_ref()) {
        ctx.renderer.line(
            MessageStyle::Info,
            "Scheduled tasks are disabled. Enable [automation.scheduled_tasks].enabled or unset VTCODE_DISABLE_CRON.",
        )?;
        return Ok(SlashCommandControl::Continue);
    }

    let store = DurableTaskStore::new_default()?;
    match action {
        ScheduleCommandAction::Interactive => {
            run_interactive_schedule_manager(&mut ctx, &store).await?
        }
        ScheduleCommandAction::Browse => browse_tasks(&mut ctx, &store, false).await?,
        ScheduleCommandAction::CreateInteractive => {
            if let Some(input) = prompt_schedule_create_input(&mut ctx).await? {
                create_task(&mut ctx, &store, input)?;
            }
        }
        ScheduleCommandAction::Create { input } => create_task(&mut ctx, &store, input)?,
        ScheduleCommandAction::DeleteInteractive => {
            delete_task_interactively(&mut ctx, &store).await?;
        }
        ScheduleCommandAction::Delete { id } => delete_task(&mut ctx, &store, &id)?,
    }

    ctx.renderer.line_if_not_empty(MessageStyle::Output)?;
    Ok(SlashCommandControl::Continue)
}

async fn run_interactive_schedule_manager(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
) -> Result<()> {
    if !super::ui::ensure_selection_ui_available(ctx, "opening scheduled tasks")? {
        return Ok(());
    }
    if !ctx.renderer.supports_inline_ui() {
        browse_tasks(ctx, store, false).await?;
        return Ok(());
    }

    loop {
        show_schedule_manager_modal(ctx, store)?;
        let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
            return Ok(());
        };

        let InlineListSelection::ConfigAction(action) = selection else {
            continue;
        };
        if action == SCHEDULE_ACTION_BACK {
            return Ok(());
        }

        let Some(action_key) = action.strip_prefix(SCHEDULE_ACTION_PREFIX) else {
            continue;
        };
        match action_key {
            "browse" => browse_tasks(ctx, store, false).await?,
            "create" => {
                if let Some(input) = prompt_schedule_create_input(ctx).await? {
                    create_task(ctx, store, input)?;
                }
            }
            "delete" => delete_task_interactively(ctx, store).await?,
            _ => {}
        }
    }
}

fn show_schedule_manager_modal(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
) -> Result<()> {
    let tasks = store.list()?;
    let task_count = tasks.len();
    let browse_subtitle = if task_count == 0 {
        "No durable scheduled tasks yet.".to_string()
    } else if task_count == 1 {
        "Browse 1 durable scheduled task.".to_string()
    } else {
        format!("Browse {task_count} durable scheduled tasks.")
    };
    let delete_subtitle = if task_count == 0 {
        "No tasks available to delete.".to_string()
    } else {
        "Select a task to delete.".to_string()
    };

    let items = vec![
        InlineListItem {
            title: "Browse scheduled tasks".to_string(),
            subtitle: Some(browse_subtitle),
            badge: Some("Recommended".to_string()),
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}browse",
                SCHEDULE_ACTION_PREFIX
            ))),
            search_value: Some("browse scheduled tasks list".to_string()),
        },
        InlineListItem {
            title: "Create scheduled task".to_string(),
            subtitle: Some(
                "Interactive flow for prompt tasks, reminders, and schedule options.".to_string(),
            ),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}create",
                SCHEDULE_ACTION_PREFIX
            ))),
            search_value: Some("create scheduled task reminder prompt".to_string()),
        },
        InlineListItem {
            title: "Delete scheduled task".to_string(),
            subtitle: Some(delete_subtitle),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}delete",
                SCHEDULE_ACTION_PREFIX
            ))),
            search_value: Some("delete remove cancel scheduled task".to_string()),
        },
        InlineListItem {
            title: "Back".to_string(),
            subtitle: Some("Close the scheduled-task manager.".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(
                SCHEDULE_ACTION_BACK.to_string(),
            )),
            search_value: Some("back close".to_string()),
        },
    ];

    ctx.handle.show_list_modal(
        "Scheduled tasks".to_string(),
        vec![
            "Manage durable scheduled tasks interactively.".to_string(),
            "Enter selects an action. Esc closes the manager.".to_string(),
        ],
        items,
        Some(InlineListSelection::ConfigAction(format!(
            "{}browse",
            SCHEDULE_ACTION_PREFIX
        ))),
        Some(InlineListSearchConfig {
            label: "Search actions".to_string(),
            placeholder: Some("browse, create, delete".to_string()),
        }),
    );
    Ok(())
}

async fn browse_tasks(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
    delete_on_select: bool,
) -> Result<()> {
    let tasks = store.list()?;
    if tasks.is_empty() {
        ctx.renderer
            .line(MessageStyle::Info, "No durable scheduled tasks.")?;
        return Ok(());
    }
    let has_overdue_tasks = tasks.iter().any(summary_is_overdue);

    if !ctx.renderer.supports_inline_ui() {
        for task in tasks {
            render_task_summary(ctx, &task)?;
        }
        if has_overdue_tasks {
            ctx.renderer
                .line(MessageStyle::Warning, DURABLE_SCHEDULER_RUNTIME_HINT)?;
        }
        return Ok(());
    }
    if !super::ui::ensure_selection_ui_available(ctx, "browsing scheduled tasks")? {
        return Ok(());
    }

    let items = tasks
        .iter()
        .map(|task| task_modal_item(task, delete_on_select))
        .collect::<Vec<_>>();
    let selected = items.first().and_then(|item| item.selection.clone());
    let title = if delete_on_select {
        "Delete scheduled task"
    } else {
        "Scheduled tasks"
    };
    let instructions = if delete_on_select {
        vec![
            "Select a task to delete it immediately.".to_string(),
            "Esc cancels without changing anything.".to_string(),
        ]
    } else {
        let mut instructions = vec![
            "Select a task to show its details in the transcript.".to_string(),
            "Esc closes this list.".to_string(),
        ];
        if has_overdue_tasks {
            instructions.insert(
                0,
                "Overdue tasks are waiting for the durable scheduler daemon.".to_string(),
            );
        }
        instructions
    };

    ctx.handle.show_list_modal(
        title.to_string(),
        instructions,
        items,
        selected,
        Some(InlineListSearchConfig {
            label: "Search tasks".to_string(),
            placeholder: Some("name, schedule, prompt, reminder".to_string()),
        }),
    );

    let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
        return Ok(());
    };
    let InlineListSelection::ConfigAction(action) = selection else {
        return Ok(());
    };

    if delete_on_select {
        if let Some(id) = action.strip_prefix(SCHEDULE_TASK_DELETE_PREFIX) {
            delete_task(ctx, store, id)?;
        }
        return Ok(());
    }

    if let Some(id) = action.strip_prefix(SCHEDULE_TASK_SHOW_PREFIX) {
        show_task_details(ctx, store, id)?;
    }
    Ok(())
}

async fn delete_task_interactively(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
) -> Result<()> {
    if !ctx.renderer.supports_inline_ui() {
        ctx.renderer.line(
            MessageStyle::Info,
            "Interactive delete is available in the TUI only. Use `/schedule delete <task-id>` here.",
        )?;
        return Ok(());
    }
    if !super::ui::ensure_selection_ui_available(ctx, "selecting a scheduled task")? {
        return Ok(());
    }
    browse_tasks(ctx, store, true).await
}

fn create_task(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
    input: ScheduleCreateInput,
) -> Result<()> {
    let default_workspace = input
        .prompt
        .as_ref()
        .map(|_| ctx.config.workspace.clone())
        .filter(|_| input.workspace.is_none());
    match store.create_from_input(input, Local::now(), default_workspace) {
        Ok(summary) => {
            ctx.renderer.line(
                MessageStyle::Info,
                &format!(
                    "Created durable scheduled task {} ({}) with {}.",
                    summary.id, summary.name, summary.schedule
                ),
            )?;
            ctx.renderer
                .line(MessageStyle::Info, DURABLE_SCHEDULER_RUNTIME_HINT)?;
            ctx.renderer.line(
                MessageStyle::Info,
                "Use `vtcode schedule install-service` to keep durable tasks running after restart.",
            )?;
        }
        Err(err) => {
            ctx.renderer.line(MessageStyle::Error, &err.to_string())?;
        }
    }
    Ok(())
}

fn delete_task(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
    id: &str,
) -> Result<()> {
    if let Some(task) = store.delete(id)? {
        ctx.renderer.line(
            MessageStyle::Info,
            &format!(
                "Deleted durable scheduled task {} ({}).",
                task.id, task.name
            ),
        )?;
    } else {
        ctx.renderer.line(
            MessageStyle::Warning,
            &format!("No durable scheduled task found for '{}'.", id),
        )?;
    }
    Ok(())
}

fn show_task_details(
    ctx: &mut SlashCommandContext<'_>,
    store: &DurableTaskStore,
    id: &str,
) -> Result<()> {
    let Some(record) = store.load_record(id)? else {
        ctx.renderer.line(
            MessageStyle::Warning,
            &format!("No durable scheduled task found for '{}'.", id),
        )?;
        return Ok(());
    };

    let action_label = match record.definition.action.kind_label() {
        "prompt" => "Prompt",
        "reminder" => "Reminder",
        _ => "Task",
    };

    ctx.renderer.line(
        MessageStyle::Info,
        &format!(
            "Scheduled task {} ({})",
            record.definition.id, record.definition.name
        ),
    )?;
    ctx.renderer.line(
        MessageStyle::Info,
        &format!("Kind: {}", record.definition.action.kind_label()),
    )?;
    ctx.renderer.line(
        MessageStyle::Info,
        &format!(
            "Schedule: {}",
            record.definition.schedule.human_description()
        ),
    )?;
    ctx.renderer.line(
        MessageStyle::Info,
        &format!("Next run: {}", format_task_time(record.runtime.next_run_at)),
    )?;
    ctx.renderer.line(
        MessageStyle::Info,
        &format!("Last run: {}", format_task_time(record.runtime.last_run_at)),
    )?;
    ctx.renderer.line(
        MessageStyle::Info,
        &format!("Status: {}", record_status_label(&record)),
    )?;
    if let Some(workspace) = record.definition.workspace.as_ref() {
        ctx.renderer.line(
            MessageStyle::Info,
            &format!("Workspace: {}", workspace.display()),
        )?;
    }
    ctx.renderer.line(
        MessageStyle::Info,
        &format!("{action_label}: {}", record.definition.action.summary()),
    )?;
    if record_is_overdue(&record) {
        ctx.renderer
            .line(MessageStyle::Warning, DURABLE_SCHEDULER_RUNTIME_HINT)?;
    }
    Ok(())
}

fn render_task_summary(
    ctx: &mut SlashCommandContext<'_>,
    task: &vtcode_core::scheduler::ScheduledTaskSummary,
) -> Result<()> {
    let next_run = task
        .next_run_at
        .map(|value| {
            value
                .with_timezone(&Local)
                .format("%Y-%m-%d %H:%M:%S")
                .to_string()
        })
        .unwrap_or_else(|| "none".to_string());
    let status = summary_status_label(task);
    ctx.renderer.line(
        MessageStyle::Info,
        &format!(
            "{}  {}  {}  next={}  status={}",
            task.id, task.name, task.schedule, next_run, status
        ),
    )?;
    Ok(())
}

fn task_modal_item(
    task: &vtcode_core::scheduler::ScheduledTaskSummary,
    delete_on_select: bool,
) -> InlineListItem {
    let next_run = task
        .next_run_at
        .map(|value| {
            value
                .with_timezone(&Local)
                .format("%Y-%m-%d %H:%M")
                .to_string()
        })
        .unwrap_or_else(|| "none".to_string());
    let status = summary_status_label(task);
    let selection = if delete_on_select {
        InlineListSelection::ConfigAction(format!("{}{}", SCHEDULE_TASK_DELETE_PREFIX, task.id))
    } else {
        InlineListSelection::ConfigAction(format!("{}{}", SCHEDULE_TASK_SHOW_PREFIX, task.id))
    };

    InlineListItem {
        title: task.name.clone(),
        subtitle: Some(format!(
            "{} • next {} • {}",
            task.schedule, next_run, status
        )),
        badge: Some(task.action_kind.clone()),
        indent: 0,
        selection: Some(selection),
        search_value: Some(format!(
            "{} {} {}",
            task.name, task.schedule, task.action_kind
        )),
    }
}

async fn prompt_schedule_create_input(
    ctx: &mut SlashCommandContext<'_>,
) -> Result<Option<ScheduleCreateInput>> {
    if !ctx.renderer.supports_inline_ui() {
        ctx.renderer.line(
            MessageStyle::Info,
            "Interactive create is available in the TUI only. Use `/schedule create --prompt ... --every ...` here.",
        )?;
        return Ok(None);
    }

    let Some(task_kind) = pick_task_kind(ctx).await? else {
        return Ok(None);
    };

    let (prompt, reminder) = match task_kind {
        InteractiveTaskKind::Prompt => (
            prompt_required_text(
                ctx,
                "Create scheduled task",
                "Enter the prompt VT Code should run on schedule.",
                "Prompt:",
                "check the deployment",
            )
            .await?,
            None,
        ),
        InteractiveTaskKind::Reminder => (
            None,
            prompt_required_text(
                ctx,
                "Create reminder",
                "Enter the reminder text VT Code should show locally.",
                "Reminder:",
                "push the release branch",
            )
            .await?,
        ),
    };
    if prompt.is_none() && reminder.is_none() {
        return Ok(None);
    }

    let Some(schedule_kind) = pick_schedule_kind(ctx).await? else {
        return Ok(None);
    };

    let (every, cron, at) = match schedule_kind {
        InteractiveScheduleKind::Every => (
            prompt_required_text(
                ctx,
                "Schedule cadence",
                "Enter a fixed interval like 10m, 2h, or 1d.",
                "Interval:",
                "10m",
            )
            .await?,
            None,
            None,
        ),
        InteractiveScheduleKind::Cron => (
            None,
            prompt_required_text(
                ctx,
                "Schedule cadence",
                "Enter a five-field cron expression in local time.",
                "Cron expression:",
                "0 9 * * 1-5",
            )
            .await?,
            None,
        ),
        InteractiveScheduleKind::At => (
            None,
            None,
            prompt_required_text(
                ctx,
                "Schedule time",
                "Enter a local one-shot time such as 15:00 or 2026-03-29 15:00.",
                "Time:",
                "15:00",
            )
            .await?,
        ),
    };
    if every.is_none() && cron.is_none() && at.is_none() {
        return Ok(None);
    }

    let name = prompt_optional_text(
        ctx,
        "Optional label",
        "Optionally give this task a short label.",
        "Label:",
        "deploy check",
    )
    .await?
    .and_then(trimmed_optional);

    let workspace = if matches!(task_kind, InteractiveTaskKind::Prompt) {
        prompt_optional_text(
            ctx,
            "Workspace",
            "Optionally override the workspace path. Leave blank to use the current session workspace.",
            "Workspace path:",
            &ctx.config.workspace.display().to_string(),
        )
        .await?
        .and_then(trimmed_optional)
        .map(PathBuf::from)
    } else {
        None
    };

    Ok(Some(ScheduleCreateInput {
        name,
        prompt,
        reminder,
        every,
        cron,
        at,
        workspace,
    }))
}

async fn pick_task_kind(ctx: &mut SlashCommandContext<'_>) -> Result<Option<InteractiveTaskKind>> {
    if !super::ui::ensure_selection_ui_available(ctx, "creating a scheduled task")? {
        return Ok(None);
    }

    ctx.handle.show_list_modal(
        "Create scheduled task".to_string(),
        vec!["Choose what VT Code should schedule.".to_string()],
        vec![
            InlineListItem {
                title: "Prompt task".to_string(),
                subtitle: Some("Run a fresh `vtcode exec` prompt on schedule.".to_string()),
                badge: Some("Recommended".to_string()),
                indent: 0,
                selection: Some(InlineListSelection::ConfigAction(
                    "schedule.kind.prompt".to_string(),
                )),
                search_value: Some("prompt exec task".to_string()),
            },
            InlineListItem {
                title: "Reminder".to_string(),
                subtitle: Some("Show a local reminder without invoking the model.".to_string()),
                badge: None,
                indent: 0,
                selection: Some(InlineListSelection::ConfigAction(
                    "schedule.kind.reminder".to_string(),
                )),
                search_value: Some("reminder local notification".to_string()),
            },
        ],
        Some(InlineListSelection::ConfigAction(
            "schedule.kind.prompt".to_string(),
        )),
        Some(InlineListSearchConfig {
            label: "Search types".to_string(),
            placeholder: Some("prompt or reminder".to_string()),
        }),
    );

    let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
        return Ok(None);
    };
    let InlineListSelection::ConfigAction(action) = selection else {
        return Ok(None);
    };

    Ok(match action.as_str() {
        "schedule.kind.prompt" => Some(InteractiveTaskKind::Prompt),
        "schedule.kind.reminder" => Some(InteractiveTaskKind::Reminder),
        _ => None,
    })
}

async fn pick_schedule_kind(
    ctx: &mut SlashCommandContext<'_>,
) -> Result<Option<InteractiveScheduleKind>> {
    if !super::ui::ensure_selection_ui_available(ctx, "choosing a schedule")? {
        return Ok(None);
    }

    ctx.handle.show_list_modal(
        "Schedule type".to_string(),
        vec!["Choose how this task should run.".to_string()],
        vec![
            InlineListItem {
                title: "Fixed interval".to_string(),
                subtitle: Some("Repeat on a cadence like 10m or 2h.".to_string()),
                badge: Some("Recommended".to_string()),
                indent: 0,
                selection: Some(InlineListSelection::ConfigAction(
                    "schedule.when.every".to_string(),
                )),
                search_value: Some("interval every fixed".to_string()),
            },
            InlineListItem {
                title: "Cron expression".to_string(),
                subtitle: Some("Use a five-field cron expression in local time.".to_string()),
                badge: None,
                indent: 0,
                selection: Some(InlineListSelection::ConfigAction(
                    "schedule.when.cron".to_string(),
                )),
                search_value: Some("cron expression".to_string()),
            },
            InlineListItem {
                title: "One-shot time".to_string(),
                subtitle: Some("Run once at a local time.".to_string()),
                badge: None,
                indent: 0,
                selection: Some(InlineListSelection::ConfigAction(
                    "schedule.when.at".to_string(),
                )),
                search_value: Some("once at time".to_string()),
            },
        ],
        Some(InlineListSelection::ConfigAction(
            "schedule.when.every".to_string(),
        )),
        Some(InlineListSearchConfig {
            label: "Search schedule types".to_string(),
            placeholder: Some("interval, cron, once".to_string()),
        }),
    );

    let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
        return Ok(None);
    };
    let InlineListSelection::ConfigAction(action) = selection else {
        return Ok(None);
    };

    Ok(match action.as_str() {
        "schedule.when.every" => Some(InteractiveScheduleKind::Every),
        "schedule.when.cron" => Some(InteractiveScheduleKind::Cron),
        "schedule.when.at" => Some(InteractiveScheduleKind::At),
        _ => None,
    })
}

async fn prompt_required_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
) -> Result<Option<String>> {
    prompt_text(ctx, title, question, freeform_label, placeholder, false).await
}

async fn prompt_optional_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
) -> Result<Option<String>> {
    prompt_text(ctx, title, question, freeform_label, placeholder, true).await
}

async fn prompt_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
    allow_empty: bool,
) -> Result<Option<String>> {
    let step = build_schedule_prompt_step(question, freeform_label, placeholder);

    let outcome = show_wizard_modal_and_wait(
        ctx.handle,
        ctx.session,
        title.to_string(),
        vec![step],
        0,
        None,
        WizardModalMode::MultiStep,
        ctx.ctrl_c_state,
        ctx.ctrl_c_notify,
    )
    .await?;

    let value = match outcome {
        WizardModalOutcome::Submitted(selections) => {
            selections
                .into_iter()
                .find_map(|selection| match selection {
                    InlineListSelection::RequestUserInputAnswer {
                        question_id,
                        selected,
                        other,
                    } if question_id == SCHEDULE_INPUT_ID => {
                        other.or_else(|| selected.first().cloned())
                    }
                    _ => None,
                })
        }
        WizardModalOutcome::Cancelled { .. } => None,
    };

    let resolved = resolve_prompt_submission(value, placeholder, allow_empty);
    if resolved.is_none() {
        ctx.renderer
            .line(MessageStyle::Info, "Input was empty. Nothing executed.")?;
    }
    Ok(resolved)
}

fn build_schedule_prompt_step(
    question: &str,
    freeform_label: &str,
    placeholder: &str,
) -> WizardStep {
    WizardStep {
        title: "Input".to_string(),
        question: question.to_string(),
        items: vec![InlineListItem {
            title: "Submit".to_string(),
            subtitle: Some(
                "Press Enter to accept the placeholder, or Tab to type a custom value.".to_string(),
            ),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::RequestUserInputAnswer {
                question_id: SCHEDULE_INPUT_ID.to_string(),
                selected: vec![],
                other: Some(String::new()),
            }),
            search_value: Some("submit input".to_string()),
        }],
        completed: false,
        answer: None,
        allow_freeform: true,
        freeform_label: Some(freeform_label.to_string()),
        freeform_placeholder: Some(placeholder.to_string()),
        freeform_default: Some(placeholder.to_string()),
    }
}

fn format_task_time(value: Option<chrono::DateTime<Utc>>) -> String {
    value
        .map(|value| {
            value
                .with_timezone(&Local)
                .format("%Y-%m-%d %H:%M:%S")
                .to_string()
        })
        .unwrap_or_else(|| "none".to_string())
}

fn trimmed_optional(value: String) -> Option<String> {
    let trimmed = value.trim().to_string();
    (!trimmed.is_empty()).then_some(trimmed)
}

fn record_is_overdue(record: &vtcode_core::scheduler::ScheduledTaskRecord) -> bool {
    durable_task_is_overdue(
        record.runtime.next_run_at,
        record.runtime.last_run_at,
        record.runtime.last_status.is_some(),
        Utc::now(),
    )
}

fn record_status_label(record: &vtcode_core::scheduler::ScheduledTaskRecord) -> String {
    if record_is_overdue(record) {
        return "overdue".to_string();
    }
    record
        .runtime
        .last_status
        .as_ref()
        .map(ToString::to_string)
        .unwrap_or_else(|| "never_run".to_string())
}

fn summary_is_overdue(task: &vtcode_core::scheduler::ScheduledTaskSummary) -> bool {
    durable_task_is_overdue(
        task.next_run_at,
        task.last_run_at,
        task.last_status.is_some(),
        Utc::now(),
    )
}

fn summary_status_label(task: &vtcode_core::scheduler::ScheduledTaskSummary) -> String {
    if summary_is_overdue(task) {
        return "overdue".to_string();
    }
    task.last_status
        .clone()
        .unwrap_or_else(|| "never_run".to_string())
}

fn resolve_prompt_submission(
    value: Option<String>,
    placeholder: &str,
    allow_empty: bool,
) -> Option<String> {
    let value = value?;
    let trimmed = value.trim();
    if !trimmed.is_empty() {
        return Some(trimmed.to_string());
    }

    let placeholder = placeholder.trim();
    if !placeholder.is_empty() {
        return Some(placeholder.to_string());
    }

    allow_empty.then_some(value)
}

#[cfg(test)]
mod tests {
    use super::{build_schedule_prompt_step, resolve_prompt_submission};

    #[test]
    fn schedule_prompt_submission_accepts_placeholder_on_empty_enter() {
        assert_eq!(
            resolve_prompt_submission(Some(String::new()), "10m", false),
            Some("10m".to_string())
        );
    }

    #[test]
    fn schedule_prompt_submission_prefers_explicit_text() {
        assert_eq!(
            resolve_prompt_submission(Some("  every 2h  ".to_string()), "10m", false),
            Some("every 2h".to_string())
        );
    }

    #[test]
    fn schedule_prompt_submission_keeps_optional_blank_without_placeholder() {
        assert_eq!(
            resolve_prompt_submission(Some(String::new()), "", true),
            Some(String::new())
        );
    }

    #[test]
    fn schedule_prompt_submission_rejects_required_blank_without_placeholder() {
        assert_eq!(
            resolve_prompt_submission(Some(String::new()), "", false),
            None
        );
    }

    #[test]
    fn schedule_prompt_step_uses_placeholder_as_default() {
        let step = build_schedule_prompt_step("Enter a cadence.", "Cadence", "10m");

        assert_eq!(step.freeform_placeholder.as_deref(), Some("10m"));
        assert_eq!(step.freeform_default.as_deref(), Some("10m"));
    }
}
