use crate::activitypub::DOMAIN;
use chrono::{DateTime, Local};
use once_cell::sync::Lazy;
use std::time::SystemTime;

static CB: Lazy<u64> = Lazy::new(|| {
    SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs()
});

static GENERATED_AT: Lazy<DateTime<Local>> = Lazy::new(|| Local::now());

pub trait TemplateCommon {
    fn domain() -> String {
        DOMAIN.to_owned()
    }

    fn stylesheet_cache_buster() -> u64 {
        *CB
    }

    fn fancy_link(text: &str, href: &str, meta: Option<&str>) -> String {
        format!(
            r#"
        <a href="{}" class="fancy-link" {}><span aria-hidden="true">{{</span>{}<span aria-hidden="true">}}</span></a>
        "#,
            href,
            meta.unwrap_or(""),
            text
        )
    }

    fn generated_at() -> &'static DateTime<Local> {
        &*GENERATED_AT
    }
}

pub mod filters {
    use std::fmt::Display;

    use chrono::{DateTime, Datelike, NaiveDate, TimeZone, Utc};

    pub fn iso_date(date: &NaiveDate) -> askama::Result<String> {
        Ok(DateTime::<Utc>::from_utc(date.and_hms(12, 0, 0), Utc)
            .format("%+:0")
            .to_string())
    }

    pub fn iso_datetime<Tz>(datetime: &DateTime<Tz>) -> askama::Result<String>
    where
        Tz: TimeZone,
        Tz::Offset: Display,
    {
        Ok(datetime.format("%+:0").to_string())
    }

    const MONTHS: &[&str; 12] = &[
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    ];

    pub fn pretty_date(date: &impl Datelike) -> askama::Result<String> {
        let month = MONTHS[date.month0() as usize];
        let suffix = match date.day() {
            1 | 21 | 31 => "st",
            2 | 22 => "nd",
            3 | 23 => "rd",
            _ => "th",
        };
        Ok(format!(
            "{} {}{}, {}",
            month,
            date.day(),
            suffix,
            date.year()
        ))
    }

    pub fn pretty_datetime<Tz>(datetime: &DateTime<Tz>) -> askama::Result<String>
    where
        Tz: TimeZone,
        Tz::Offset: Display,
    {
        Ok(format!(
            "{} {}",
            datetime.format("%-I:%M:%S %p"),
            pretty_date(datetime)?
        ))
    }

    pub fn reading_time(words: &u32) -> askama::Result<u32> {
        let wpm = 225.0;
        return Ok((*words as f32 / wpm).max(1.0) as u32);
    }
}
