use rudof_config::TomlConfig;
use rudof_iri::IriS;
use rudof_rdf::rdf_core::vocabs::RdfsVocab;
use serde::{Deserialize, Serialize};
use shex_validation::ShExConfig;
use std::path::{Path, PathBuf};

use crate::ShEx2UmlConfig;

#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ShEx2HtmlConfig {
    #[serde(rename = "title")]
    pub(crate) title: String,
    #[serde(rename = "landing_page")]
    pub(crate) landing_page_name: String,
    #[serde(rename = "shape_template")]
    pub(crate) shape_template_name: String,
    #[serde(rename = "template_folder", skip_serializing_if = "Option::is_none")]
    pub(crate) template_folder: Option<String>,
    #[serde(rename = "css_file")]
    pub(crate) css_file_name: String,
    #[serde(rename = "target_folder")]
    pub(crate) target_folder: PathBuf,
    #[serde(rename = "property_color")]
    pub(crate) color_property_name: String,
    #[serde(rename = "annotation_label", skip_serializing_if = "Vec::is_empty")]
    pub(crate) annotation_label: Vec<IriS>,
    #[serde(rename = "replace_iri_by_label")]
    pub(crate) replace_iri_by_label: bool,
    #[serde(rename = "embed_svg_schema")]
    pub(crate) embed_svg_schema: bool,
    #[serde(rename = "embed_svg_shape")]
    pub(crate) embed_svg_shape: bool,
    #[serde(rename = "shex2uml", skip_serializing)]
    pub(crate) shex2uml: ShEx2UmlConfig,
    #[serde(rename = "shex", skip_serializing)]
    pub(crate) shex: ShExConfig, // TODO - Maybe remove, a copy of ShexConfig is in Shex2umlConfig
}

impl ShEx2HtmlConfig {
    pub fn new() -> Self {
        Self {
            title: Self::default_title(),
            landing_page_name: Self::default_landing_page_name(),
            shape_template_name: Self::default_shape_template_name(),
            template_folder: Self::default_template_folder(),
            css_file_name: Self::default_css_file_name(),
            target_folder: Self::default_target_folder(),
            color_property_name: Self::default_color_property_name(),
            annotation_label: Self::default_annotation_label(),
            replace_iri_by_label: Self::default_replace_iri_by_label(),
            embed_svg_schema: Self::default_embed_svg_schema(),
            embed_svg_shape: Self::default_embed_svg_shape(),
            shex2uml: Self::default_shex2uml(),
            shex: Self::default_shex(),
        }
    }

    pub fn with_title(mut self, title: String) -> Self {
        self.title = title;
        self
    }

    pub fn with_landing_page_name(mut self, name: String) -> Self {
        self.landing_page_name = name;
        self
    }

    pub fn with_shape_template_name(mut self, name: String) -> Self {
        self.shape_template_name = name;
        self
    }

    pub fn with_template_folder(mut self, folder: Option<String>) -> Self {
        self.template_folder = folder;
        self
    }

    pub fn with_css_file_name(mut self, name: String) -> Self {
        self.css_file_name = name;
        self
    }

    pub fn with_target_folder<P: AsRef<Path>>(mut self, folder: P) -> Self {
        self.target_folder = folder.as_ref().to_path_buf();
        self
    }

    pub fn with_color_property_name(mut self, color: String) -> Self {
        self.color_property_name = color;
        self
    }

    pub fn with_annotation_label(mut self, labels: Vec<IriS>) -> Self {
        self.annotation_label = labels;
        self
    }

    pub fn with_replace_iri_by_label(mut self, flag: bool) -> Self {
        self.replace_iri_by_label = flag;
        self
    }

    pub fn with_embed_svg_schema(mut self, flag: bool) -> Self {
        self.embed_svg_schema = flag;
        self
    }

    pub fn with_embed_svg_shape(mut self, flag: bool) -> Self {
        self.embed_svg_shape = flag;
        self
    }

    pub fn with_shex2uml(mut self, cfg: ShEx2UmlConfig) -> Self {
        self.shex2uml = cfg;
        self
    }

    pub fn with_shex(mut self, cfg: ShExConfig) -> Self {
        self.shex = cfg;
        self
    }
}

impl ShEx2HtmlConfig {
    pub fn title(&self) -> &String {
        &self.title
    }

    pub fn landing_page_name(&self) -> &String {
        &self.landing_page_name
    }

    pub fn shape_template_name(&self) -> &String {
        &self.shape_template_name
    }

    pub fn template_folder(&self) -> Option<&String> {
        self.template_folder.as_ref()
    }

    pub fn css_file_name(&self) -> &String {
        &self.css_file_name
    }

    pub fn target_folder(&self) -> &PathBuf {
        &self.target_folder
    }

    pub fn color_property_name(&self) -> &String {
        &self.color_property_name
    }

    pub fn annotation_label(&self) -> &Vec<IriS> {
        self.annotation_label.as_ref()
    }

    pub fn replace_iri_by_label(&self) -> bool {
        self.replace_iri_by_label
    }

    pub fn embed_svg_schema(&self) -> bool {
        self.embed_svg_schema
    }

    pub fn embed_svg_shape(&self) -> bool {
        self.embed_svg_shape
    }

    pub fn shex2uml(&self) -> &ShEx2UmlConfig {
        &self.shex2uml
    }

    /// Get the ShEx config
    pub fn shex(&self) -> &ShExConfig {
        &self.shex
    }
}

impl ShEx2HtmlConfig {
    pub fn landing_page(&self) -> PathBuf {
        self.target_folder.as_path().join(self.landing_page_name.as_str())
    }
}

/// Serde stuff
#[allow(dead_code)]
#[rustfmt::skip]
impl ShEx2HtmlConfig {
    #[inline] fn default_title() -> String { "ShEx schema".to_string() }
    #[inline] fn default_landing_page_name() -> String { "index.html".to_string() }
    #[inline] fn default_shape_template_name() -> String { "shape.html".to_string() }
    #[inline] fn default_template_folder() -> Option<String> { None }
    #[inline] fn default_css_file_name() -> String { "shex2html.css".to_string() }
    #[inline] fn default_target_folder() -> PathBuf { Path::new(".").to_path_buf() }
    #[inline] fn default_color_property_name() -> String { "blue".to_string() }
    #[inline] fn default_annotation_label() -> Vec<IriS> { vec![ RdfsVocab::rdfs_label() ] }
    #[inline] fn default_replace_iri_by_label() -> bool { true }
    #[inline] fn default_embed_svg_schema() -> bool { true }
    #[inline] fn default_embed_svg_shape() -> bool { true }
    #[inline] fn default_shex2uml() -> ShEx2UmlConfig { ShEx2UmlConfig::default() }
    #[inline] fn default_shex() -> ShExConfig { ShExConfig::default() }
}

impl Default for ShEx2HtmlConfig {
    fn default() -> Self {
        Self::new()
    }
}

impl TomlConfig for ShEx2HtmlConfig {}

#[cfg(test)]
mod tests {
    use super::ShEx2HtmlConfig;
    use rudof_config::TomlConfig;

    #[test]
    fn defaults() {
        assert_eq!(ShEx2HtmlConfig::default().title(), &ShEx2HtmlConfig::default_title());
    }

    #[test]
    fn partial_toml_fills_remaining_defaults() {
        let c = ShEx2HtmlConfig::from_toml_str(r#"title = "My schema""#).unwrap();
        assert_eq!(c.title(), "My schema");
        assert_eq!(c.landing_page_name(), &ShEx2HtmlConfig::default_landing_page_name());
    }

    #[test]
    fn toml_round_trip() {
        let c = ShEx2HtmlConfig::default().with_title("My schema".to_string());
        let s = c.to_toml_string().unwrap();
        let d = ShEx2HtmlConfig::from_toml_str(&s).unwrap();
        assert_eq!(c, d);
    }
}
