use crossbeam_channel::Sender;

use pueue_lib::network::message::*;
use pueue_lib::state::{GroupStatus, SharedState};
use pueue_lib::task::{Task, TaskStatus};

use super::*;
use crate::ok_or_return_failure_message;
use crate::state_helper::save_state;

/// Invoked when calling `pueue add`.
/// Queues a new task to the state.
/// If the start_immediately flag is set, send a StartMessage to the task handler.
pub fn add_task(message: AddMessage, sender: &Sender<Message>, state: &SharedState) -> Message {
    let mut state = state.lock().unwrap();
    if let Err(message) = ensure_group_exists(&mut state, &message.group) {
        return message;
    }

    let starting_status = if message.stashed || message.enqueue_at.is_some() {
        TaskStatus::Stashed {
            enqueue_at: message.enqueue_at,
        }
    } else {
        TaskStatus::Queued
    };

    // Ensure that specified dependencies actually exist.
    let not_found: Vec<_> = message
        .dependencies
        .iter()
        .filter(|id| !state.tasks.contains_key(id))
        .collect();
    if !not_found.is_empty() {
        return create_failure_message(format!(
            "Unable to setup dependencies : task(s) {not_found:?} not found",
        ));
    }

    // Create a new task and add it to the state.
    let mut task = Task::new(
        message.command,
        message.path,
        message.envs,
        message.group,
        starting_status,
        message.dependencies,
        message.label,
    );
    // Sort and deduplicate dependency id.
    task.dependencies.sort_unstable();
    task.dependencies.dedup();

    // Check if the task's group is paused before we pass it to the state
    let group_status = state
        .groups
        .get(&task.group)
        .expect("We ensured that the group exists.")
        .status;
    let group_is_paused = matches!(group_status, GroupStatus::Paused);

    // Add the task and persist the state.
    let task_id = state.add_task(task);
    ok_or_return_failure_message!(save_state(&state));

    // Notify the task handler, in case the client wants to start the task immediately.
    if message.start_immediately {
        sender
            .send(Message::Start(StartMessage {
                tasks: TaskSelection::TaskIds(vec![task_id]),
                children: false,
            }))
            .expect(SENDER_ERR);
    }

    // Create the customized response for the client.
    let mut response = if message.print_task_id {
        task_id.to_string()
    } else if let Some(enqueue_at) = message.enqueue_at {
        let enqueue_at = enqueue_at.format("%Y-%m-%d %H:%M:%S");
        format!("New task added (id {task_id}). It will be enqueued at {enqueue_at}")
    } else {
        format!("New task added (id {task_id}).")
    };

    // Notify the user if the task's group is paused
    if !message.print_task_id && group_is_paused {
        response.push_str("\nThe group of this task is currently paused!")
    }

    create_success_message(response)
}
