use anyhow::{anyhow, Result};
use serde::{Serialize, Serializer};
use std::collections::HashMap;
use uuid::Uuid;

#[derive(Default, Serialize, Clone)]
pub struct Job {
    job_type: JobType,
    title: String,
    progress: f32,
    state: State,
}

impl Job {
    pub fn new<S: AsRef<str>>(job_type: S, title: S) -> Self {
        return Job {
            job_type: String::from(job_type.as_ref()),
            title: String::from(title.as_ref()),
            ..Default::default()
        };
    }
}

pub type JobType = String;

#[derive(Serialize, Clone)]
pub enum State {
    InProgress,
    Done,
}

impl Default for State {
    fn default() -> Self {
        State::InProgress
    }
}

#[derive(Default)]
pub struct JobContainer {
    jobs: HashMap<JobId, Job>,
}

#[derive(Clone, Hash, PartialEq, Eq)]
pub struct JobId {
    uuid: Uuid,
}

impl From<Uuid> for JobId {
    fn from(uuid: Uuid) -> Self {
        JobId { uuid }
    }
}

impl Serialize for JobId {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(format!("{}", self.uuid).as_str())
    }
}

impl JobContainer {
    pub fn add_job(&mut self, job: Job) -> Result<JobId> {
        let uuid = Uuid::new_v4();
        self.jobs.insert(JobId::from(uuid), job);
        Ok(JobId::from(uuid))
    }

    pub fn get_jobs(&self) -> HashMap<JobId, Job> {
        self.jobs.clone()
    }

    pub fn update_progress(&mut self, id: &JobId, progress: f32) -> Result<()> {
        if let Some(job) = self.jobs.get_mut(id) {
            job.progress = progress;
            if progress >= 100.0 {
                job.state = State::Done;
            }
            Ok(())
        } else {
            Err(anyhow!("No such job."))
        }
    }
}
