use std::{cmp::Ordering, sync::OnceLock};

use regex::Regex;

use crate::error_log;

pub(crate) fn parse_float_version(str: &str) -> Option<f32> {
    static REGEX: OnceLock<Regex> = OnceLock::new();
    let regex = REGEX.get_or_init(|| Regex::new(r"^[0-9]+\.[0-9]+").expect("Float version regex is invalid"));
    let version_match = regex.captures(str)?.get(0)?;
    return version_match.as_str().parse().ok();
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct SemVer {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
}

impl SemVer {
    pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
        return Self { major, minor, patch };
    }

    pub fn parse(str: &str) -> Option<SemVer> {
        let mut versions = [0, 0, 0];
        str.split('.')
            .take(3)
            .enumerate()
            .try_for_each(|(i, part)| match part.parse() {
                Ok(num) => {
                    versions[i] = num;
                    Ok(())
                }
                Err(err) => {
                    error_log!("Failed to parse version '{str}': {err}");
                    Err(())
                }
            })
            .ok()?;
        return Some(Self {
            major: versions[0],
            minor: versions[1],
            patch: versions[2],
        });
    }
}

impl PartialEq for SemVer {
    fn eq(&self, other: &Self) -> bool {
        return (self.major == other.major) && (self.minor == other.minor) && (self.patch == other.patch);
    }
}

impl PartialOrd for SemVer {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some((self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch)))
    }
}
