Thunderstore CLI and Northstar mod dev helper
use anyhow::{Context, Result};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs};

use super::FILE_NAME;

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
pub(crate) struct Manifest {
    pub name: String,
    pub description: String,
    pub version: Version,
    pub load_priority: u16,
    pub convars: Vec<ConvarObj>,
    pub scripts: Vec<ScriptObj>,
    pub localisation: Vec<String>,
}

impl Manifest {
    pub fn new(name: String, description: String) -> Self {
        Manifest {
            name,
            description,
            version: Version::new(0, 1, 0),
            load_priority: 0,
            convars: vec![],
            scripts: vec![],
            localisation: vec![],
        }
    }

    pub fn save(&self) -> Result<()> {
        let target = std::env::current_dir()?.join("mod.json");
        let raw = serde_json::to_string_pretty(self)?;
        fs::write(target, &raw).context("Failed to write file")?;

        Ok(())
    }
}

#[derive(Deserialize, Serialize, Debug, Clone, Default)]
#[serde(rename_all = "PascalCase")]
pub(crate) struct ConvarObj {
    pub name: String,
    pub default_value: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
pub(crate) struct ScriptObj {
    pub path: String,
    pub run_on: String,
    #[serde(flatten)]
    pub client_callback: Option<HashMap<String, String>>,
    #[serde(flatten)]
    pub server_callback: Option<HashMap<String, String>>,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
pub(crate) struct Mod {
    name: String,
    author: String,
    version: Version,
}

impl Mod {
    pub(crate) fn new(author: String, name: String) -> Self {
        Mod {
            author,
            name,
            version: Version::new(0, 1, 0),
        }
    }
    // pub fn read() -> Result<Self> {
    //     let p = std::env::current_dir()?.join(FILE_NAME);
    //     let raw = fs::read_to_string(&p)?;
    //     ron::from_str(&raw).context("Unable to parse index")
    // }

    pub fn save(&self) -> Result<()> {
        let cfg = ron::ser::PrettyConfig::new().compact_arrays(true);
        let p = std::env::current_dir()?.join(FILE_NAME);
        let raw = ron::ser::to_string_pretty(self, cfg)?;

        fs::write(&p, &raw).context("Failed to write index")?;

        Ok(())
    }
}

// #[derive(Deserialize, Serialize, Debug, Clone)]
// struct Version {
//     major: u8,
//     minor: u8,
//     patch: u8,
// }

// impl FromStr for Version {
//     type Err = String;

//     fn from_str(s: &str) -> Result<Self, Self::Err> {
//         let re = Regex::new(r"(\d+)\.(\d+)\.(\d+)").unwrap();
//         if let Some(c) = re.captures(s) {
//             let major = c.get(1).unwrap().as_str().parse::<u8>().unwrap();
//             let minor = c.get(2).unwrap().as_str().parse::<u8>().unwrap();
//             let patch = c.get(3).unwrap().as_str().parse::<u8>().unwrap();

//             Ok(Version {
//                 major,
//                 minor,
//                 patch,
//             })
//         } else {
//             Err(String::from("Unable to parse version"))
//         }
//     }
// }

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