use semver::Version;
use crate::token::TokenTrimmer;

#[derive(Debug, PartialEq)]
pub struct VersionList(Vec<Version>);

impl VersionList {
    pub fn from_tag_list(tag_names: &Vec<String>,
                         token_trimmer: Option<TokenTrimmer>) -> VersionList {
        let trimmed_tag_names = Self::trimmed_tag_names(tag_names, token_trimmer);
        let mut versions: Vec<Version> = trimmed_tag_names.iter()
            .filter_map(|tag| lenient_semver::parse(tag).ok())
            .collect();
        versions.sort();
        VersionList(versions)
    }

    fn trimmed_tag_names<'a>(tag_names: &'a Vec<String>,
                             token_trimmer: Option<TokenTrimmer>) -> Vec<&'a str> {
        match token_trimmer {
            None => tag_names.iter().map(|tag| tag.as_str()).collect(),
            Some(trimmer) => tag_names.iter()
                .map(|name| trimmer.trim_text(name))
                .collect()
        }
    }

    pub fn latest_version(&self) -> Option<Version> {
        self.0.last().map(Version::clone)
    }
}

#[cfg(test)]
mod test {
    use crate::token::TokenTrimmer;
    use crate::version::list::VersionList;

    #[test]
    fn from_tag_list_empty() {
        let version_list = VersionList::from_tag_list(&vec![], None);
        let expected = VersionList(vec![]);
        assert_eq!(expected, version_list)
    }

    #[test]
    fn from_tag_list_no_trimmer() {
        let tags = vec![
            "10.0.0",
            "2.0.0",
            "xxxx",
            "0.1.0",
            "0.0.1",
        ];
        let string_tags = tags_to_string(&tags);
        let version_list = VersionList::from_tag_list(&string_tags, None);
        let expected_tags = vec![
            "0.0.1",
            "0.1.0",
            "2.0.0",
            "10.0.0",
        ];
        let expected_versions = expected_tags.iter()
            .filter_map(|tag| lenient_semver::parse(tag).ok())
            .collect::<Vec<_>>();
        let expected = VersionList(expected_versions);
        assert_eq!(expected, version_list)
    }

    #[test]
    fn from_tag_list_with_trimmer() {
        let tags = vec![
            "release_10.0.0_xxx",
            "2.0.0",
            "xxxx",
            "release_0.1.0_xxx",
            "release_0.0.1_xxx",
        ];
        let string_tags = tags_to_string(&tags);
        let token_trimmer = TokenTrimmer::new("release_$version_xxx", "$version");
        let version_list = VersionList::from_tag_list(&string_tags, token_trimmer);
        let expected_tags = vec![
            "0.0.1",
            "0.1.0",
            "2.0.0",
            "10.0.0",
        ];
        let expected_versions = expected_tags.iter()
            .filter_map(|tag| lenient_semver::parse(tag).ok())
            .collect::<Vec<_>>();
        let expected = VersionList(expected_versions);
        assert_eq!(expected, version_list)
    }

    #[test]
    fn from_tag_list_with_trimmer_trims_v() {
        let tags = vec![
            "v0.0.1",
            "v10.0.0",
            "v2.0.0"
        ];
        let string_tags = tags_to_string(&tags);
        let token_trimmer = TokenTrimmer::new("v$version", "$version");
        let version_list = VersionList::from_tag_list(&string_tags, token_trimmer);
        let expected_tags = vec![
            "0.0.1",
            "2.0.0",
            "10.0.0",
        ];
        let expected_versions = expected_tags.iter()
            .filter_map(|tag| lenient_semver::parse(tag).ok())
            .collect::<Vec<_>>();
        let expected = VersionList(expected_versions);
        assert_eq!(expected, version_list)
    }

    #[test]
    fn latest_version_empty_version_list() {
        let version_list = VersionList::from_tag_list(&vec![], None);
        assert_eq!(None, version_list.latest_version())
    }

    #[test]
    fn latest_version_with_versions() {
        let tags = vec![
            "10.0.0",
            "2.0.0",
            "0.0.1",
        ];
        let string_tags = tags_to_string(&tags);
        let version_list = VersionList::from_tag_list(&string_tags, None);
        let expected_version = lenient_semver::parse("10.0.0")
            .expect("expected version should parse");
        assert_eq!(Some(expected_version), version_list.latest_version())
    }

    #[test]
    fn latest_version_with_versions_with_v() {
        let tags = vec![
            "v10.0.0",
            "v2.0.0",
            "v0.0.1",
        ];
        let string_tags = tags_to_string(&tags);
        let version_list = VersionList::from_tag_list(&string_tags, None);
        let expected_version = lenient_semver::parse("v10.0.0")
            .expect("expected version should parse");
        assert_eq!(Some(expected_version), version_list.latest_version())
    }

    fn tags_to_string(tags: &Vec<&str>) -> Vec<String> {
        tags.iter()
            .map(|tag| tag.to_string())
            .collect()
    }
}

