use std::path::PathBuf;

use thiserror::Error;

use crate::markdown::BrokenWikiLink;

#[derive(Debug, Error)]
pub enum Error {
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    #[error("config parse error: {0}")]
    Config(#[from] toml::de::Error),
    #[error("missing YAML frontmatter")]
    MissingFrontmatter,
    #[error("frontmatter parse error: {0}")]
    FrontmatterParse(#[from] serde_yml::Error),
    #[error("failed to load {path}")]
    LoadPage {
        path: PathBuf,
        #[source]
        source: Box<Error>,
    },
    #[error("slug collision: '{slug}' claimed by {path1} and {path2}")]
    SlugCollision {
        slug: String,
        path1: PathBuf,
        path2: PathBuf,
    },
    #[error("failed to load theme from {path}")]
    ThemeLoad {
        path: PathBuf,
        #[source]
        source: Box<Error>,
    },
    #[error("theme is missing required templates: {}", missing.join(", "))]
    ThemeMissingTemplates { missing: Vec<String> },
    #[error("template render error: {0}")]
    Tera(#[from] tera::Error),
    #[error("broken wiki-links found:\n{}", format_broken_links(.0))]
    BrokenWikiLinks(Vec<BrokenWikiLink>),
    #[error("file watcher error: {0}")]
    Notify(#[from] notify::Error),
    #[error("refusing unsafe path '{}': {reason}", path.display())]
    UnsafeOutputPath { path: PathBuf, reason: &'static str },
    #[error("{} is not valid UTF-8", path.display())]
    NotUtf8 { path: PathBuf },
    #[error("invalid config: {field}: {message}")]
    InvalidConfig {
        field: &'static str,
        message: String,
    },
    #[error("scaffold error: {message}")]
    Scaffold { message: String },
    #[error("failed to generate favicon from '{}': {reason}", path.display())]
    Favicon { path: PathBuf, reason: String },
}

fn format_broken_links(links: &[BrokenWikiLink]) -> String {
    links
        .iter()
        .map(|link| {
            format!(
                "  - page \"{}\" references missing wiki-link: \"{}\"",
                link.source, link.target
            )
        })
        .collect::<Vec<_>>()
        .join("\n")
}
