use std::io::{Error, ErrorKind};

use vdir::transport::dir::dir_transport::DirTransport;
use vfile::util::location::Location;

use crate::chopper::error::Error::Io;
use crate::chopper::error::Result;
use crate::input::dir::location_provider::LocationProvider;

pub struct DirLocationProvider {
    files_in_reverse: Vec<Location>,
}

impl DirLocationProvider {
    pub fn new(
        dir_transport: &Box<(dyn DirTransport)>,
        loc: &Location,
    ) -> Result<DirLocationProvider> {
        if !dir_transport.is_dir(loc) {
            return Err(Io(Error::new(
                ErrorKind::InvalidData,
                format!("directory expected: {}", loc),
            )));
        };

        let mut files: Vec<Location> = dir_transport
            .read_dir(loc)?
            .filter(|e| !dir_transport.is_dir(e))
            .collect();

        files.sort_by(|a, b| b.cmp(a));

        Ok(DirLocationProvider {
            files_in_reverse: files,
        })
    }
}

impl Iterator for DirLocationProvider {
    type Item = Location;

    fn next(&mut self) -> Option<Location> {
        self.files_in_reverse.pop()
    }
}

impl LocationProvider for DirLocationProvider {}

#[cfg(test)]
mod tests {
    use vdir::transport::dir::dir_transport::DirTransport;
    use vdir::transport::dir::file::DirFileTransport;

    use crate::input::dir::dir_location_provider::DirLocationProvider;

    #[test]
    fn test() {
        let transport: Box<dyn DirTransport> = Box::new(DirFileTransport {});
        let loc = &"./tests/input/multi_file".into();
        let mut provider = DirLocationProvider::new(&transport, loc).unwrap();

        let next = provider.next();
        assert!(next.is_some());
        assert_eq!(next.unwrap(), loc.join("1.csv"));

        let next = provider.next();
        assert!(next.is_some());
        assert_eq!(next.unwrap(), loc.join("2.csv"));

        let next = provider.next();
        assert!(next.is_some());
        assert_eq!(next.unwrap(), loc.join("3.csv"));

        let next = provider.next();
        assert!(next.is_none());
    }
}
