//! Jobber database

pub(crate) use error::*;

use crate::{
    entities::{Client, Company, Invoice, Job, Tag, Worker},
    jobber::*,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

mod create;
mod delete;
mod edit;
mod error;
mod get;
mod iter;
mod work;

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub(crate) struct Database {
    company: Option<Company>,
    workers: HashMap<String, Worker>,
    clients: HashMap<String, Client>,
    jobs: Vec<Job>,
    tags: HashMap<String, Tag>,
    invoices: Vec<Invoice>,
}

impl Database {
    fn get_job_mut(&mut self, job_id: usize) -> Result<&mut Job> {
        if job_id >= self.jobs.len() {
            return Err(Error::JobNotFound(job_id));
        }
        Ok(&mut self.jobs[job_id])
    }
}

impl Jobber for Database {}

#[cfg(test)]
mod test {
    use crate::entities::{Address, DateTime};

    use super::*;

    #[test]
    fn work() {
        let mut database = Database::default();

        let worker_id: String = "mw".into();
        let client_id: String = "mc".into();
        database
            .create_worker(&worker_id, |_| Ok(Worker::new("My Worker".into())))
            .unwrap();
        database
            .create_client(&client_id, |_| {
                Ok(Client::new(
                    Address {
                        name: "My Client".into(),
                        street: "Street".into(),
                        zip: "ZIP".into(),
                        city: "City".into(),
                        ..Default::default()
                    },
                    None,
                    Default::default(),
                ))
            })
            .unwrap();
        database
            .create_job(None, |job| {
                *job = Job::new(
                    worker_id,
                    client_id,
                    "job subject".into(),
                    Default::default(),
                );
                Ok(true)
            })
            .unwrap();
        let pos = database.jobs.len() - 1;
        let _running = database
            .start_work(pos, DateTime::now(), Some("subject"), ["tag"])
            .unwrap();
        let work = database
            .end_work(
                pos,
                DateTime::now(),
                Mod::Append("more subject"),
                ["another_tag"],
            )
            .unwrap();
        assert_eq!(work.subject, "subject\nmore subject");
        assert_eq!(work.tags.len(), 2);
    }
}
