use chrono::prelude::*;
use derive_more::Constructor;
use markdown::{to_html_with_options, CompileOptions, Options};
use regex::Regex;
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{self, read_dir, read_to_string};
use std::path::PathBuf;
use tera::{Context, Tera};

pub fn read_src_files(src_dir: &str) -> Vec<SrcMD> {
    let mut files: Vec<SrcMD> = Vec::new();
    for file in read_dir(src_dir).expect("Cant read src dir") {
        let file = file.unwrap();
        let kv = get_kv(read_to_string(file.path()).unwrap()).0;
        let title = kv.get("title").unwrap();
        let date = kv.get("date").unwrap();
        files.push(SrcMD::new(
            file.path(),
            title.to_string(),
            DateTime::parse_from_str(&format!("{date} 00:00:00 +00:00"), "%Y-%m-%d %H:%M:%S %z")
                .unwrap(),
            file.path().file_stem().unwrap().to_os_string(),
        ))
    }

    files
}

pub fn write_to_fs(html: String, output_dir: &String, file_name: &String) {
    fs::write(format!("{output_dir}/{file_name}.html"), html)
        .unwrap_or_else(|_| panic!("Error writing {file_name}"));
}

#[derive(Constructor)]
pub struct SrcMD {
    pub path: PathBuf,
    pub title: String,
    pub date: DateTime<FixedOffset>,
    pub file_name: OsString,
}

pub fn generate_blog_entry(markdown: String, template_dir: &String) -> (String, String) {
    let markdown = markdown.clone();

    let mut tera = Tera::new(&format!("{template_dir}/*")).unwrap();
    tera.autoescape_on(vec![]);

    let (mut key_value, markdown) = get_kv(markdown);

    let html_markdown = to_html_with_options(
        &markdown,
        &Options {
            compile: CompileOptions {
                allow_dangerous_html: true,
                allow_dangerous_protocol: true,
                ..CompileOptions::default()
            },
            ..Options::gfm()
        },
    )
    .unwrap();

    key_value.insert("blog_content".to_string(), html_markdown.clone());

    let context = Context::from_serialize(&key_value).unwrap();

    let templated_html = tera
        .render(key_value.get("template").unwrap(), &context)
        .unwrap();
    (templated_html, html_markdown)
}

pub fn get_kv(markdown: String) -> (HashMap<String, String>, String) {
    let re_key_value = Regex::new(r"(?ms)---(.*)---(?:\n)").unwrap();

    let key_value_string = re_key_value
        .find(markdown.as_str())
        .expect("Can't find key value map in markdown");

    let content_markdown = re_key_value
        .replace(markdown.clone().as_str(), "")
        .to_string();

    let mut key_value: HashMap<String, String> = HashMap::new();

    for line in key_value_string.as_str().lines() {
        if line == "---" {
            continue;
        }
        key_value.insert(
            line.split(':')
                .collect::<Vec<&str>>()
                .first()
                .unwrap()
                .trim()
                .to_string(),
            line.split(':')
                .collect::<Vec<&str>>()
                .get(1)
                .unwrap()
                .trim()
                .to_string(),
        );
    }

    (key_value, content_markdown)
}