//! Jobber database interface

use crate::{
    database::Result,
    entities::{
        Client, Company, DateTime, Duration, Invoice, Job, NaiveDate, RunningWork, Tag, Work,
        Worker,
    },
};
use std::collections::HashMap;

pub(crate) trait JobberCreate {
    fn create_company<T>(&mut self, f: impl FnOnce(&mut Company) -> Result<T>) -> Result<T>;
    fn create_worker<T>(&mut self, id: &str, f: impl FnOnce(&mut Worker) -> Result<T>)
    -> Result<T>;
    fn create_client<T>(&mut self, id: &str, f: impl FnOnce(&mut Client) -> Result<T>)
    -> Result<T>;
    fn create_job<T>(
        &mut self,
        pos: Option<usize>,
        f: impl FnOnce(&mut Job) -> Result<T>,
    ) -> Result<T>;
    fn create_tag<T>(&mut self, name: &str, f: impl FnOnce(&mut Tag) -> Result<T>) -> Result<T>;
    fn create_invoice(
        &mut self,
        job_ids: impl Iterator<Item = usize>,
        subject: Option<String>,
        until: Option<NaiveDate>,
    ) -> Result<usize>;
}

pub(crate) trait JobberDelete {
    fn delete_company(&mut self) -> Result<()>;
    fn delete_worker(&mut self, id: &str, delete: bool) -> Result<()>;
    fn delete_client(&mut self, id: &str, delete: bool) -> Result<()>;
    fn delete_job(&mut self, id: usize, delete: bool) -> Result<()>;
    fn delete_tag(&mut self, id: &str, delete: bool) -> Result<()>;
    fn delete_invoice(&mut self, id: usize, delete: bool) -> Result<()>;
    fn delete_work(&mut self, job_id: usize, work_pos: usize, delete: bool) -> Result<()>;
}

pub(crate) trait JobberGet {
    fn get_company(&self) -> Result<&Company>;
    fn get_worker(&self, id: &str) -> Result<&Worker>;
    fn get_client(&self, id: &str) -> Result<&Client>;
    fn get_job(&self, pos: usize) -> Result<&Job>;
    fn get_tag(&self, name: &str) -> Result<&Tag>;
    fn get_tags(&self) -> &HashMap<String, Tag>;
    fn get_invoice(&self, no: usize) -> Result<&Invoice>;
    fn get_full_client_name(&self, id: &str) -> Result<String> {
        Ok(format!("{} ({})", self.get_client(id)?.address.name, id))
    }
    fn get_full_worker_name(&self, id: &str) -> Result<String> {
        Ok(format!("{} ({})", self.get_worker(id)?.full_name, id))
    }
}

pub(crate) trait JobberEdit {
    fn edit_company<T>(&mut self, f: impl FnOnce(&mut Company) -> Result<T>) -> Result<T>;
    fn edit_worker<T>(&mut self, id: &str, f: impl FnOnce(&mut Worker) -> Result<T>) -> Result<T>;
    fn edit_client<T>(&mut self, id: &str, f: impl FnOnce(&mut Client) -> Result<T>) -> Result<T>;
    fn edit_job<T>(&mut self, pos: usize, f: impl FnOnce(&mut Job) -> Result<T>) -> Result<T>;
    fn edit_tag<T>(&mut self, name: &str, f: impl FnOnce(&mut Tag) -> Result<T>) -> Result<T>;
}

pub(crate) trait JobberWork {
    fn start_work(
        &mut self,
        job: usize,
        start: DateTime,
        subject: Option<impl ToString>,
        tags: impl IntoIterator<Item = impl ToString>,
    ) -> Result<RunningWork>;
    fn end_work(
        &mut self,
        job: usize,
        end: DateTime,
        subject: Mod<impl ToString>,
        tags: impl IntoIterator<Item = impl ToString>,
    ) -> Result<Work>;
    fn add_work(
        &mut self,
        job: usize,
        start: DateTime,
        end: DateTime,
        subject: impl ToString,
        tags: impl IntoIterator<Item = impl ToString>,
    ) -> Result<Work>;
    fn cancel_work(&mut self, job: usize) -> Result<RunningWork>;
    fn sort(&mut self, job_id: usize) -> Result<()>;
    fn duration_by_worker(&self, worker_id: impl ToString) -> Duration;
    fn duration_by_client(&self, client_id: impl ToString) -> Duration;
    fn duration_by_day(&self, job_id: Option<usize>, day: DateTime) -> Duration;
    fn work_range(&self, job_id: &Option<usize>, deleted: Deleted) -> (DateTime, DateTime);
}

#[derive(Copy, Clone)]
pub(crate) enum Deleted {
    No,
    Yes,
    All,
}

impl From<bool> for Deleted {
    fn from(value: bool) -> Self {
        match value {
            true => Self::Yes,
            false => Self::No,
        }
    }
}

impl Deleted {
    pub(crate) fn check(&self, deleted: bool) -> bool {
        match self {
            Deleted::No => !deleted,
            Deleted::Yes => deleted,
            Deleted::All => true,
        }
    }
}

pub(crate) trait JobberIter {
    fn iter_workers(&self, deleted: Deleted) -> impl Iterator<Item = (&String, &Worker)>;
    fn iter_clients(&self, deleted: Deleted) -> impl Iterator<Item = (&String, &Client)>;
    fn iter_jobs(&self, deleted: Deleted) -> impl Iterator<Item = (usize, &Job)>;
    fn iter_tags(&self, deleted: Deleted) -> impl Iterator<Item = (&String, &Tag)>;
    fn iter_invoices(&self, deleted: Deleted) -> impl Iterator<Item = (usize, &Invoice)>;
}

pub(crate) trait Jobber:
    JobberCreate + JobberDelete + JobberGet + JobberEdit + JobberWork + JobberIter
{
}

pub(crate) enum Mod<T = String> {
    None,
    Set(T),
    Replace(T),
    Append(T),
}
