use crate::SemVer;

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

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

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

impl PartialOrd for Rapid {
    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),
            Some(Ordering::Equal) => self.minor.partial_cmp(&other.minor),
            None => None,
        }
    }
}

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

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

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

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

impl PartialOrd<SemVer> for Rapid {
    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,
        }
    }
}

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

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

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

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

impl Error for ParseRapidError {}

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

impl FromStr for Rapid {
    type Err = ParseRapidError;

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

impl TryFrom<Version> for Rapid {
    type Error = ParseRapidError;

    fn try_from(value: Version) -> Result<Self, Self::Error> {
        match value {
            Version::SemVer(s) => {
                if s.patch == 0 {
                    Ok(Self {
                        major: s.major,
                        minor: s.minor,
                    })
                } else {
                    Err(ParseRapidError)
                }
            }
            Version::Rapid(s) => Ok(s),
            Version::Number(major) => Ok(Self { major, minor: 0 }),
            Version::Git(_) => Err(ParseRapidError),
        }
    }
}

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

    #[test]
    fn parse_semver() {
        assert_eq!(
            "93.0".parse::<Rapid>(),
            Ok(Rapid {
                major: 93,
                minor: 0,
            })
        );
    }

    #[test]
    fn rapid_num_eq() {
        let rapid = Rapid {
            major: 42,
            minor: 0,
        };
        assert_eq!(rapid, 42);
    }

    #[test]
    fn rapid_num_gt() {
        let rapid = Rapid {
            major: 1,
            minor: 42,
        };
        assert!(rapid > 1);
    }

    #[test]
    fn rapid_semver_eq() {
        let rapid = Rapid {
            major: 42,
            minor: 69,
        };
        let semver = SemVer {
            major: 42,
            minor: 69,
            patch: 0,
        };
        assert_eq!(rapid, semver);
    }

    #[test]
    fn rapid_semver_lt() {
        let rapid = Rapid {
            major: 42,
            minor: 69,
        };
        let semver = SemVer {
            major: 42,
            minor: 69,
            patch: 1,
        };
        assert!(rapid < semver);
    }
}
