use crate::entities::{DateTime, Duration, Error, Job, Money, Result, Scheme, Work};
use derive_more::Deref;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deref, derive_more::DerefMut, Clone, Default, Serialize, Deserialize)]
pub(crate) struct Bill {
    pub(crate) worker: String,
    pub(crate) subject: String,
    #[deref]
    #[deref_mut]
    pub(crate) work: Vec<Work>,
    pub(crate) scheme: Scheme,
    pub(crate) deleted: bool,
}

impl Bill {
    pub(crate) fn worked(&self) -> Duration {
        self.scheme
            .measure_work(self.work.iter().map(|work| &work.work_time))
    }

    pub(crate) fn revenue(&self) -> Result<Money> {
        self.scheme.measure_revenue(self.worked())
    }
}

pub(crate) trait Billable {
    /// Create a bill out of the job
    ///
    /// # Arguments
    /// `until`: An optional date until work is moved out of the job into the bill
    fn bill(&mut self, job_id: usize, until: Option<DateTime>) -> Result<Bill>;
}

impl Billable for Job {
    fn bill(&mut self, job_id: usize, until: Option<DateTime>) -> Result<Bill> {
        let work_time: Vec<_> = self
            .work
            .extract_if(.., |w| {
                if let Some(until) = until {
                    w.end() < until
                } else {
                    true
                }
            })
            .collect();

        Ok(Bill {
            worker: self.worker.clone(),
            subject: self.subject.clone(),
            work: work_time,
            scheme: if let Some(scheme) = &self.scheme {
                scheme.clone()
            } else {
                return Err(Error::JobMissingScheme(job_id));
            },
            deleted: false,
        })
    }
}
