use std::fmt;

use tracing::instrument;

use crate::vfs::ToDynDir;

use super::{entries, Dir, DynDir, Entries, Query, VPath, VPathBuf};

pub struct Overlay {
    base: DynDir,
    overlay: DynDir,
}

impl Overlay {
    pub fn new(base: DynDir, overlay: DynDir) -> Self {
        Self { base, overlay }
    }

    #[instrument("Overlay::dir", skip(self))]
    fn dir(&self, path: &VPath) -> Vec<VPathBuf> {
        let mut dir = entries(&self.base, path);
        dir.append(&mut entries(&self.overlay, path));
        dir.sort();
        dir.dedup();
        dir
    }
}

impl Dir for Overlay {
    fn query(&self, path: &VPath, query: &mut Query) {
        query.provide(|| Entries(self.dir(path)));

        self.overlay.query(path, query);
        self.base.query(path, query);
    }
}

impl fmt::Debug for Overlay {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Overlay({:?}, {:?})", self.base, self.overlay)
    }
}

pub fn layered_dir(layers: &[DynDir]) -> DynDir {
    match layers {
        [] => ().to_dyn(),
        [dir] => dir.clone(),
        [left, right] => Overlay::new(left.clone(), right.clone()).to_dyn(),
        [left, right, rest @ ..] => {
            let mut overlay = Overlay::new(left.clone(), right.clone());
            for dir in rest {
                overlay = Overlay::new(overlay.to_dyn(), dir.clone());
            }
            overlay.to_dyn()
        }
    }
}
