//! Author data.

use rusqlite::{named_params, Connection, Row};
use serde::Serialize;

use super::{
    error::DataStoreError,
    pagination::{Pagination, SortOrder},
};

/// Author in calibre.
#[derive(Debug, Clone, Serialize)]
pub struct Author {
    /// Id in database.
    pub id: u64,
    /// Full name.
    pub name: String,
    /// Full name for sorting.
    pub sort: String,
}

impl Author {
    fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
        Ok(Self {
            id: row.get(0)?,
            name: row.get(1)?,
            sort: row.get(2)?,
        })
    }

    /// Fetch author data from calibre, starting at `cursor`, fetching up to an amount of `limit` and
    /// ordering by `sort_order`.
    pub fn multiple(
        conn: &Connection,
        limit: u64,
        cursor: Option<&str>,
        sort_order: &SortOrder,
    ) -> Result<Vec<Self>, DataStoreError> {
        let pagination = Pagination::new("sort", cursor, limit, *sort_order);
        pagination.paginate(
            conn,
            "SELECT id, name, sort FROM authors",
            &[],
            Self::from_row,
        )
    }

    /// Get the author to a book with id `id`.
    pub fn book_author(conn: &Connection, id: u64) -> Result<Self, DataStoreError> {
        let mut stmt = conn.prepare(
            "SELECT authors.id, authors.name, authors.sort FROM authors \
                INNER JOIN books_authors_link ON authors.id = books_authors_link.author \
                WHERE books_authors_link.book = (:id)",
        )?;
        let params = named_params! { ":id": id };
        Ok(stmt.query_row(params, Self::from_row)?)
    }

    /// Fetch a single author with id `id`.
    pub fn scalar_author(conn: &Connection, id: u64) -> Result<Self, DataStoreError> {
        let mut stmt = conn.prepare("SELECT id, name, sort FROM authors WHERE id = (:id)")?;
        let params = named_params! { ":id": id };
        Ok(stmt.query_row(params, Self::from_row)?)
    }

    /// Check if there are more authors before the specified cursor.
    pub fn has_previous_authors(
        conn: &Connection,
        sort_name: &str,
    ) -> Result<bool, DataStoreError> {
        Pagination::has_prev_or_more(conn, "authors", sort_name, &SortOrder::DESC)
    }

    /// Check if there are more authors after the specified cursor.
    pub fn has_more_authors(conn: &Connection, sort_name: &str) -> Result<bool, DataStoreError> {
        Pagination::has_prev_or_more(conn, "authors", sort_name, &SortOrder::ASC)
    }
}