use std::{
    collections::HashMap,
    fs::{self, File, OpenOptions},
    io::{self, ErrorKind, Write},
    path::PathBuf,
    process,
};

use serde::{Deserialize, Serialize};

use super::{class::ClassName, class_type::ClassTypeName, grade::Grade, grade_type::GradeTypeName};

#[derive(Debug, Deserialize, Serialize)]
pub struct GradesDataFile {
    pub classes: HashMap<ClassTypeName, HashMap<ClassName, HashMap<GradeTypeName, Vec<Grade>>>>,
}

impl GradesDataFile {
    pub fn data_init(file: &PathBuf) {
        fn file_check(file: &PathBuf) -> io::Result<bool> {
            if let Err(e) = File::open(file) {
                if e.kind() == ErrorKind::NotFound {
                    Ok(true)
                } else {
                    Err(e)
                }
            } else {
                //file already exists
                Ok(false)
            }
        }
        match file_check(file) {
            Ok(value) => {
                if value {
                    let output = GradesDataFile {
                        classes: HashMap::new(),
                    };
                    if let Err(e) = output.to_ron_file(file) {
                        eprintln!(
                            "Error with serializing the data file at path: {}",
                            file.display()
                        );
                        eprintln!("Error: {e}");
                    };
                }
            }
            Err(e) => {
                eprintln!(
                    "Error with accessing the data file at path: {}",
                    file.display()
                );
                eprintln!("Error: {e}");
            }
        };
    }

    pub fn to_ron_file(&self, file: &PathBuf) -> io::Result<()> {
        let ron_config = ron::ser::PrettyConfig::new()
            .new_line("\n".to_owned())
            .indentor("    ".to_owned())
            .separator(" ".to_owned())
            .struct_names(true)
            .separate_tuple_members(true)
            .enumerate_arrays(false)
            .compact_arrays(false)
            .extensions(ron::extensions::Extensions::empty());

        match ron::ser::to_string_pretty(self, ron_config) {
            Err(e) => {
                eprintln!("Error: {e}");
            }
            Ok(output) => {
                fs::write(file, [])?;
                let mut file = OpenOptions::new().append(true).open(file).unwrap();
                writeln!(
                file,
                "// DO NOT EDIT!\n// This File is automatically generated through the cli interface",
            )?;
                file.write_all(output.as_bytes())?;
            }
        }
        Ok(())
    }

    pub fn from_ron_file(file: &PathBuf) -> io::Result<GradesDataFile> {
        let file_string = fs::read_to_string(file)?;
        match ron::from_str::<GradesDataFile>(&file_string) {
            Err(e) => {
                eprintln!(
                    "Error: {}, at line: {}, column: {}",
                    e.code, e.position.line, e.position.col
                );
                process::exit(1);
            }
            Ok(output) => Ok(output),
        }
    }
}
