/// Builds the website into the output directory
mod base_template;

use relative_path::RelativePath;
use std::fs;
use tera::{Context, Tera};
use toml::Value;

use crate::{
    config_handler::Config,
    content_processor::{process_md, process_toml, split_front_matter},
    debug_print::dprintln,
};

pub(crate) fn build(root_dir: &str, drafts: bool, output: &str) {
    println!("Building for project in: {}", root_dir);

    // Clear output folder
    if fs::metadata(output).is_ok() {
        fs::remove_dir_all(output).expect("Couldn't clear output directory");
    }
    fs::create_dir(output).expect("Couldn't create output directory");

    // Compile templates
    let template_path = RelativePath::new("./templates/**/*")
        .to_logical_path(root_dir)
        .to_string_lossy()
        .to_string();
    // strip local dir prefix - Tera doesn't like it for some reason
    let template_path = if let Some(x) = template_path.strip_prefix(".") {
        &x[1..]
    } else {
        template_path.as_str()
    };
    dprintln!("Building templates in: {}", &template_path);
    let mut tera = match Tera::parse(template_path.replace("\\", "/").as_str()) {
        Ok(t) => t,
        Err(e) => {
            eprintln!("Parsing error: {}", e);
            return;
        }
    };

    // Add built-in base template
    if let Err(e) = tera.add_raw_template("base.html", base_template::BASE) {
        eprintln!("Error adding the base template: {}", e);
    }
    tera.build_inheritance_chains().expect("Couldn't build inheritance chain");

    // loop through all files in content & process them individually
    let files = match RelativePath::new("./content").to_logical_path(root_dir).read_dir() {
        Ok(files) => files,
        Err(e) => panic!("Encountered an error while parsing content folder: {}", e),
    };

    for file in files {
        let filename = &file.as_ref().unwrap().file_name().to_string_lossy().to_string();
        let filename = filename.as_str();
        dprintln!("Processing file: {}", filename);

        let content = if let Ok(content) = fs::read_to_string(&file.as_ref().unwrap().path()) {
            content.as_str().replace('\r', "")
        } else {
            eprintln!("Failed reading {}", filename);
            return;
        };

        let loaded_toml;
        let processed_markdown: Option<String>;
        if filename.ends_with(".md") {
            // Separate markdown and toml
            let (toml, markdown) = split_front_matter(content.as_str()).unwrap();
            loaded_toml = process_toml(toml);
            processed_markdown = Some(process_md(markdown));
        } else if filename.ends_with(".toml") {
            // Process toml
            loaded_toml = process_toml(&content);
            processed_markdown = None;
        } else {
            continue; // skip non toml/md files
        }
        // Decide whether to compile drafts
        match loaded_toml.get_key_value("draft") {
            Some((_, Value::Boolean(true))) => {
                if drafts {
                    () // compile drafts when --drafts set
                } else {
                    continue;
                } // skip cycle
            }
            _ => (), // always compile non drafts
        }

        // Pass parsed md/toml to the templating engine
        let mut context = Context::new();
        context.insert("Config", Config::global());
        context.insert("Page", &loaded_toml);
        context.insert("Content", &processed_markdown);

        let to_render = if loaded_toml.contains_key("template") {
            match loaded_toml.get_key_value("template") {
                Some((_, Value::String(v))) => v,
                _ => {
                    dprintln!("Couldn't find 'template' variable");
                    "base.html"
                }
            }
        } else {
            "base.html"
        };

        // Render to file
        match tera.render(to_render, &context) {
            Ok(to_write) => {
                let temp = file.unwrap().path();
                let relative_filepath =
                    temp.strip_prefix(root_dir).unwrap().strip_prefix("content").unwrap();
                let mut filepath =
                    RelativePath::new(&relative_filepath.to_string_lossy()).to_logical_path(output);
                filepath.set_extension("html");

                match fs::write(filepath, to_write) {
                    Ok(_) => {
                        dprintln!("File write succesful");
                    }
                    Err(e) => {
                        eprintln!(
                            "Encountered error: {} while writing processed file to output",
                            e
                        );
                        return;
                    }
                }
            }
            Err(e) => {
                eprintln!("Error while rendering file: {} into a template: {}", filename, e);
                return;
            }
        }
    }

    // loop through all files in assets
    let files = match RelativePath::new("./assets").to_logical_path(root_dir).read_dir() {
        Ok(files) => files,
        Err(_e) => panic!("Encountered an error while parsing assets folder"),
    };
    for file in files {
        let filename = (&file).as_ref().unwrap().file_name().to_string_lossy().to_string();
        let filename = filename.as_str();

        match fs::copy(
            file.unwrap().path(),
            RelativePath::new(&format!("./{}", filename)).to_logical_path(output),
        ) {
            Ok(_) => (),
            Err(e) => eprintln!("Error copying asset file {}: {}", &filename, e),
        }
    }
}
