use std::{env, fmt, fs};

use dsx_common::config::DsxConfig;

use crate::templates::{self, Dsa, Dsc, Template};

// ----------  new project ----------------------------------------------------
pub fn new_project(args: &[String]) {
    // get project details from args.
    let lang = Language::from_args(args);
    let lib = args.contains(&"--lib".to_string());
    let name = project_name(args).unwrap_or_else(|| {
        eprintln!("Error: --name argument required");
        std::process::exit(1);
    });

    let project_path = env::current_dir().unwrap().join(name);
    let src_path = project_path.join("src");

    fs::create_dir_all(&src_path).expect("Failed to create project directory");

    let config_template = DsxConfig::new(name);
    fs::write(
        project_path.join("Dsx.toml"),
        toml::to_string(&config_template).unwrap(),
    )
    .expect("Unable to write default config");

    let (path, template) = match lang {
        Language::Unknown | Language::Dsa => {
            (src_path.join("main.dsa"), Dsa::create(name, lib))
        }
        Language::Dsc => (src_path.join("main.dsc"), Dsc::create(name, lib)),
    };

    fs::write(path, template).expect("Unable to write DSA file");

    fs::create_dir_all(src_path.join("lib")).expect("Failed to create lib directory");
    fs::write(
        src_path.join("./lib/print.dsa"),
        include_str!("../templates/dsa/print.dsa"),
    )
    .expect("Failed to create print.dsa");
    fs::write(
        src_path.join("./lib/maths.dsa"),
        include_str!("../templates/dsa/maths.dsa"),
    )
    .expect("Failed to create maths.dsa");
    fs::write(
        src_path.join("./lib/serial.dsa"),
        include_str!("../templates/dsa/serial.dsa"),
    )
    .expect("Failed to create maths.dsa");

    println!(
        "Created new {} project in {}.",
        lang,
        src_path.parent().unwrap().display()
    );
}

// helpers

enum Language {
    Unknown,
    Dsa,
    Dsc,
}

impl Language {
    fn from_args(args: &[String]) -> Self {
        let mut lang = Language::Unknown;
        for i in 0..args.len() {
            if args[i] == "--lang" && i + 1 < args.len() {
                match args[i + 1].as_str() {
                    "dsa" => lang = Language::Dsa,
                    "dsc" => lang = Language::Dsc,
                    _ => {
                        eprintln!("Error: Invalid language argument");
                        std::process::exit(1);
                    }
                }
            }
        }
        lang
    }
}

impl fmt::Display for Language {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unknown => write!(f, "Unknown"),
            Self::Dsa => write!(f, "Dsa"),
            Self::Dsc => write!(f, "Dsc"),
        }
    }
}

pub fn project_name(args: &[String]) -> Option<&str> {
    for i in 0..args.len() {
        if args[i] == "--name" && i + 1 < args.len() {
            return Some(&args[i + 1]);
        }
    }

    None
}
