use {
    crate::{Rapid, Version},
    serde::{Deserialize, Serialize},
    std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr},
};

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct SemVer {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
}

impl fmt::Display for SemVer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
    }
}

impl PartialOrd for SemVer {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.major.partial_cmp(&other.major) {
            Some(Ordering::Greater) => Some(Ordering::Greater),
            Some(Ordering::Less) => Some(Ordering::Less),
            None => None,
            Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) {
                Some(Ordering::Greater) => Some(Ordering::Greater),
                Some(Ordering::Less) => Some(Ordering::Less),
                None => None,
                Some(Ordering::Equal) => self.patch.partial_cmp(&other.patch),
            },
        }
    }
}

impl Ord for SemVer {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match self.major.cmp(&other.major) {
            Ordering::Greater => Ordering::Greater,
            Ordering::Less => Ordering::Less,
            Ordering::Equal => match self.minor.cmp(&other.minor) {
                Ordering::Greater => Ordering::Greater,
                Ordering::Less => Ordering::Less,
                Ordering::Equal => self.patch.cmp(&other.patch),
            },
        }
    }
}

impl PartialEq<Rapid> for SemVer {
    fn eq(&self, other: &Rapid) -> bool {
        self.major == other.major && self.minor == other.minor && self.patch == 0
    }
}

impl PartialOrd<Rapid> for SemVer {
    fn partial_cmp(&self, other: &Rapid) -> Option<std::cmp::Ordering> {
        match self.major.partial_cmp(&other.major) {
            Some(Ordering::Greater) => Some(Ordering::Greater),
            Some(Ordering::Less) => Some(Ordering::Less),
            None => None,
            Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) {
                Some(Ordering::Greater) => Some(Ordering::Greater),
                Some(Ordering::Less) => Some(Ordering::Less),
                None => None,
                Some(Ordering::Equal) => match self.patch {
                    0 => Some(Ordering::Equal),
                    _ => Some(Ordering::Greater),
                },
            },
        }
    }
}

impl PartialEq<u32> for SemVer {
    fn eq(&self, other: &u32) -> bool {
        self.major == *other && self.minor == 0 && self.patch == 0
    }
}

impl PartialOrd<u32> for SemVer {
    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
        match self.major.cmp(other) {
            Ordering::Greater => Some(Ordering::Greater),
            Ordering::Less => Some(Ordering::Less),
            Ordering::Equal => {
                if self.minor == 0 && self.patch == 0 {
                    Some(Ordering::Equal)
                } else {
                    Some(Ordering::Greater)
                }
            }
        }
    }
}

impl PartialEq<SemVer> for u32 {
    fn eq(&self, other: &SemVer) -> bool {
        other.eq(self)
    }
}

impl PartialOrd<SemVer> for u32 {
    fn partial_cmp(&self, other: &SemVer) -> Option<Ordering> {
        match other.partial_cmp(self) {
            Some(Ordering::Less) => Some(Ordering::Greater),
            Some(Ordering::Greater) => Some(Ordering::Less),
            Some(Ordering::Equal) => Some(Ordering::Equal),
            None => None,
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct ParseSemVerError;

impl fmt::Display for ParseSemVerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Error parsing SemVer")
    }
}

impl Error for ParseSemVerError {}

impl From<ParseIntError> for ParseSemVerError {
    fn from(_value: ParseIntError) -> Self {
        Self
    }
}

impl FromStr for SemVer {
    type Err = ParseSemVerError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let split = s.split('.').collect::<Vec<_>>();
        match split.len() {
            3 => {
                let major = split.first().unwrap().parse::<u32>()?;
                let minor = split.get(1).unwrap().parse::<u32>()?;
                let patch = split.get(2).unwrap().parse::<u32>()?;
                Ok(Self {
                    major,
                    minor,
                    patch,
                })
            }
            _ => Err(ParseSemVerError),
        }
    }
}

impl TryFrom<Version> for SemVer {
    type Error = ParseSemVerError;
    fn try_from(value: Version) -> Result<Self, Self::Error> {
        match value {
            Version::SemVer(s) => Ok(SemVer {
                major: s.major,
                minor: s.minor,
                patch: s.patch,
            }),
            Version::Rapid(r) => Ok(SemVer {
                major: r.major,
                minor: r.minor,
                patch: 0,
            }),
            Version::Number(n) => Ok(Self {
                major: n,
                minor: 0,
                patch: 0,
            }),
            Version::Git(_) => Err(ParseSemVerError),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn parse_semver() {
        assert_eq!(
            "1.0.3".parse::<SemVer>(),
            Ok(SemVer {
                major: 1,
                minor: 0,
                patch: 3
            })
        );
    }

    #[test]
    fn cmp_semver_num_gt() {
        let sem = "42.69.0".parse::<SemVer>().unwrap();
        let num = 42;
        assert!(sem > num);
    }

    #[test]
    fn cmp_semver_num_eq() {
        let sem = "42.0.0".parse::<SemVer>().unwrap();
        let num = 42;
        assert_eq!(sem, num);
    }
}
