use std::cmp::Ordering;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::{ffi::OsString, path::Path};

use heck::ToTitleCase;

use crate::FolgezettelId;
use crate::error::ParseFileNameError;

const TITLE_SEPARATORS: [char; 2] = ['-', '_'];

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct Zettel {
    pub file: String,
    pub extension: OsString,
    pub id: FolgezettelId,
    pub title: String,
}

impl Zettel {
    pub fn parse(input: impl AsRef<str>) -> Result<Self, ParseFileNameError> {
        let input = input.as_ref();
        let path = Path::new(input);

        let stem = path
            .file_stem()
            .and_then(|value| value.to_str())
            .ok_or(ParseFileNameError::MissingStem)?;

        let extension = path
            .extension()
            .ok_or(ParseFileNameError::MissingFileExtension)?;

        let (id, title) = stem
            .split_once(TITLE_SEPARATORS)
            .ok_or(ParseFileNameError::MissingTitleSeparator)?;

        if title.is_empty() {
            return Err(ParseFileNameError::EmptyTitle);
        }

        Ok(Self {
            extension: extension.to_owned(),
            file: input.to_owned(),
            id: FolgezettelId::parse(id)?,
            title: title.to_title_case(),
        })
    }
}

impl Ord for Zettel {
    fn cmp(&self, other: &Self) -> Ordering {
        self.id
            .cmp(&other.id)
            // In case of duplicate id, compare raw file name
            .then_with(|| self.file.cmp(&other.file))
    }
}

impl Display for Zettel {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
        write!(formatter, "{} {}", self.id, self.title)
    }
}
