use anyhow::Error;
use std::collections::HashMap;
use warp::hyper::StatusCode;

use crate::{
    image::MyImage, load::load_from_path, misc::Misc, post::Post, resp::Response, style::Style,
};

pub struct Blog {
    pages: Vec<Post>,
    posts: Vec<Post>,
    imgs: Vec<MyImage>,
    misc: Vec<Misc>,
    custom_style: Option<Style>,
}

impl Blog {
    pub fn new() -> Result<Self, Error> {
        let pages = load_from_path("pages", "pages")?;
        let posts = load_from_path("posts", "posts")?;
        let imgs = load_from_path("assets/img", "")?;
        let misc = load_from_path("assets/misc", "misc")?;
        let custom_style = Style::load_if_exists("assets/style.css")?;

        Ok(Self {
            pages,
            posts,
            imgs,
            misc,
            custom_style,
        })
    }

    fn home(&mut self) -> Result<String, Error> {
        self.posts.sort();
        self.posts.reverse();

        let mut content =
            r#"<div class="centered"><h1>Stephen's Site</h1></div>Welcome to my humble corner of the Internet! Occasionally I will do something kind of interesting and post about it here. Bear with me - I'm a developer, not a writer, so I'm bad at keeping this place up to date! For a more complete list of my work, please check out my <a href="https://gitlab.scd31.com/stephen">Git repository</a>.<h2>Recent Posts</h2><table>"#
                .to_string();

        for post in &self.posts {
            content.push_str(&post.link()?);
        }

        content.push_str("</table>");

        Ok(dress_page("Stephen's Site", &content, &self.pages))
    }
}

pub struct RenderedBlog {
    pages: HashMap<String, Response>,
    not_found: Response,
}

impl RenderedBlog {
    pub fn get(&self, path: &str) -> (StatusCode, Response) {
        self.pages
            .get(path)
            .map(|x| (StatusCode::OK, x.clone()))
            .unwrap_or((StatusCode::NOT_FOUND, self.not_found.clone()))
    }
}

impl TryFrom<Blog> for RenderedBlog {
    type Error = Error;

    fn try_from(mut b: Blog) -> Result<Self, Error> {
        let mut pages = HashMap::new();

        for p in &b.posts {
            let body = dress_page(&p.title, &p.html()?, &b.pages);

            pages.insert(p.url.clone(), Response::html(body));
        }

        for p in &b.pages {
            let body = dress_page(&p.title, &p.html()?, &b.pages);

            pages.insert(p.url.clone(), Response::html(body));
        }

        let mut style = include_str!("assets/style.css").to_string();
        if let Some(s) = &b.custom_style {
            style.push_str(&s.content);
        }

        pages.insert("/style.css".to_string(), Response::css(style));

        let home = b.home()?;
        pages.insert("/".to_string(), Response::html(home));

        for img in b.imgs {
            let (original_url, original, thumbnail_url, thumbnail) = img.into_responses()?;
            pages.insert(thumbnail_url, thumbnail);
            pages.insert(original_url, original);
        }

        for m in b.misc {
            pages.insert(m.url.clone(), m.response());
        }

        let not_found = Response::html(dress_page(
            "Page not found",
            include_str!("assets/404.html"),
            &b.pages,
        ));

        dbg!(pages.keys());

        Ok(Self { not_found, pages })
    }
}

fn dress_page(title: &str, content: &str, pages: &[Post]) -> String {
    let mut page_links = String::new();
    for p in pages {
        page_links.push_str(&p.link_short());
    }

    format!(
        r#"<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/style.css" /><title>{title}</title></head><body><a href="/">Home</a>{page_links}<hr>{content}</body></html>"#
    )
}