use std::sync::Arc;

use calibre_db::data::pagination::SortOrder;
use poem::{
    handler,
    web::{Data, WithContentType},
    IntoResponse,
};
use time::OffsetDateTime;

use crate::{
    app_state::AppState,
    data::book::Book,
    handlers::error::HandlerError,
    opds::{
        author::Author, content::Content, entry::Entry, feed::Feed, link::Link,
        media_type::MediaType, relation::Relation,
    },
};

fn create_feed(
    now: OffsetDateTime,
    self_link: Link,
    mut additional_links: Vec<Link>,
    entries: Vec<Entry>,
) -> Feed {
    let author = Author {
        name: "Thallian".to_string(),
        uri: "https://code.vanwa.ch/shu/rusty-library".to_string(),
        email: None,
    };
    let mut links = vec![
        Link {
            href: "/opds".to_string(),
            media_type: MediaType::Navigation,
            rel: Relation::Start,
            title: Some("Home".to_string()),
            count: None,
        },
        self_link,
    ];
    links.append(&mut additional_links);

    Feed {
        title: "rusty-library".to_string(),
        id: "rusty:catalog".to_string(),
        updated: now,
        icon: "favicon.ico".to_string(),
        author,
        links,
        entries,
    }
}

#[handler]
pub async fn books(state: Data<&Arc<AppState>>) -> Result<WithContentType<String>, poem::Error> {
    let books: Vec<Book> = state
        .calibre
        .books(u32::MAX.into(), None, &SortOrder::ASC)
        .map(|x| {
            x.iter()
                .filter_map(|y| Book::full_book(y, &state))
                .collect()
        })
        .map_err(HandlerError::DataError)?;

    let entries: Vec<Entry> = books.into_iter().map(Entry::from).collect();
    let now = OffsetDateTime::now_utc();

    let self_link = Link {
        href: "/opds/books".to_string(),
        media_type: MediaType::Navigation,
        rel: Relation::Myself,
        title: None,
        count: None,
    };
    let feed = create_feed(now, self_link, vec![], entries);
    let xml = feed.as_xml().map_err(HandlerError::OpdsError)?;

    Ok(xml.with_content_type("application/atom+xml"))
}

#[handler]
pub async fn handler(state: Data<&Arc<AppState>>) -> Result<WithContentType<String>, poem::Error> {
    let now = OffsetDateTime::now_utc();

    let self_link = Link {
        href: "/opds".to_string(),
        media_type: MediaType::Navigation,
        rel: Relation::Myself,
        title: None,
        count: None,
    };
    let books_entry = Entry {
        title: "Books".to_string(),
        id: "rusty:books".to_string(),
        updated: now,
        content: Content {
            media_type: MediaType::Text,
            content: "Index of all books".to_string(),
        },
        author: None,
        links: vec![Link {
            href: "/opds/books".to_string(),
            media_type: MediaType::Navigation,
            rel: Relation::Subsection,
            title: None,
            count: None,
        }],
    };

    let feed = create_feed(now, self_link, vec![], vec![books_entry]);
    let xml = feed.as_xml().map_err(HandlerError::OpdsError)?;

    Ok(xml.with_content_type("application/atom+xml"))
}