use std::collections::HashMap;

use crate::{
    commands::{
        Cli, Config, Result,
        edit::{edit_scheme::edit_scheme, obligatory},
    },
    entities::{Job, Scheme},
    jobber::{Deleted, JobberEdit, JobberIter},
    print_tip,
    views::JobView,
};
use clap::Parser;
use color_print::cprintln;
use inquire::{Confirm, Select, Text};

/// Edit job
#[derive(Parser, Debug)]
pub(crate) struct EditJob {
    /// Jobs position
    id: usize,
}

impl EditJob {
    pub(crate) fn run(&self, cli: &Cli) -> Result<()> {
        let config = Config::load()?;
        let mut database = cli.open_database()?;
        loop {
            let workers: Vec<_> = database
                .iter_workers(Deleted::All)
                .map(|(id, _)| id.clone())
                .collect();
            let clients: HashMap<_, _> = database
                .iter_clients(Deleted::All)
                .map(|(id, client)| (id.clone(), client.scheme.clone()))
                .collect();
            if !database.edit_job(self.id, |job| edit_job(job, workers, clients))? {
                cprintln!("Nothing changed.");
                return Ok(());
            }
            println!();
            println!("{}", JobView::new(self.id, &config, &database, cli.ansi())?);
            match Confirm::new("Is this information correct?")
                .with_default(true)
                .with_help_message("cancel change with ESC")
                .prompt_skippable()?
            {
                Some(true) => {
                    cprintln!("Successfully edited job {}.", self.id);
                    print_tip!(cli);
                    print_tip!(cli, "Use <s>jobber show job</> to see the job data.");
                    print_tip!(cli, "Use <s>jobber undo</> to undo the change.");
                    cli.close_database(database)?;
                    return Ok(());
                }
                Some(false) => (),
                None => {
                    cprintln!("Database remained unchanged.");
                    return Ok(());
                }
            }
        }
    }
}

pub(crate) fn edit_job(
    job: &mut Job,
    workers: Vec<String>,
    clients: HashMap<String, Scheme>,
) -> crate::database::Result<bool> {
    let orig = job.clone();

    println!();
    println!("Please enter the job details...");
    println!();
    if workers.len() > 1 {
        job.worker = Select::new("Worker?", workers)
            .with_starting_filter_input(&orig.worker)
            .prompt()?;
    } else {
        job.worker = workers.first().expect("internal error").clone();
        println!("> Worker: {}", job.worker)
    }
    if clients.len() > 1 {
        job.client = Select::new("Client?", clients.keys().cloned().collect())
            .with_starting_filter_input(&orig.client)
            .prompt()?;
    } else {
        job.client = clients.iter().next().expect("internal error").0.clone();
        println!("> Client: {}", job.client)
    }
    job.subject = Text::new("Subject?")
        .with_validator(obligatory)
        .with_default(&orig.subject)
        .prompt()?;

    if job.scheme.is_none() {
        job.scheme = clients.get(&job.client).cloned();
    }

    if let Some(scheme) = &mut job.scheme {
        edit_scheme(scheme)?;
    } else {
        unreachable!("internal error")
    }

    Ok(orig != *job)
}
