use std::{ffi::OsString, fmt::Display, process};

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GradeTypeName(pub String);

#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GradeTypeGroupName(pub String);

#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GradeTypeGroup {
    pub name: GradeTypeGroupName,
    pub weight: GradeTypeWeight,
}

#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GradeTypeWeight(pub String);

#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GradeType {
    pub name: GradeTypeName,
    pub group: GradeTypeGroupName,
}

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

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

impl From<&str> for GradeTypeName {
    fn from(value: &str) -> Self {
        GradeTypeName(value.to_owned())
    }
}

impl From<&str> for GradeTypeWeight {
    fn from(value: &str) -> Self {
        GradeTypeWeight(value.to_owned())
    }
}

impl From<&str> for GradeTypeGroupName {
    fn from(value: &str) -> Self {
        GradeTypeGroupName(value.to_owned())
    }
}

impl From<OsString> for GradeTypeName {
    fn from(value: OsString) -> Self {
        let value_as_str = match value.to_str() {
            None => {
                eprintln!("The supplied class, can not be converted to utf-8; check if it contain not valid bytes.");
                process::exit(1);
            }
            Some(value) => value,
        };
        GradeTypeName(value_as_str.to_owned())
    }
}
