use serde::Serialize;

use crate::constants::USER_ME;
use crate::types::{ChapterId, UserId};
use crate::v2::builder::UserRequestError;
use crate::v2::responses::UserChaptersMarkerResponseType;
use crate::v2::MangaDexV2;
use crate::MangaDexClient;

/// Builder for the `/api/v2/user/{id}/marker` endpoint.
pub struct SetUserChaptersReadBuilder<'a> {
    md_client: &'a MangaDexV2,
    user_id: Option<UserId>,
    me: bool,
    chapters: Vec<ChapterId>,
    mark_read: bool,
}

impl<'a> SetUserChaptersReadBuilder<'a> {
    pub fn new(md_client: &'a MangaDexV2) -> Self {
        Self {
            md_client,
            user_id: None,
            me: false,
            chapters: Vec::new(),
            mark_read: false,
        }
    }

    /// Set the numeric user ID.
    ///
    /// This takes priority over the `me` field if both are set.
    pub fn user_id(&mut self, user_id: UserId) -> &mut Self {
        self.user_id = Some(user_id);
        self
    }

    /// Set `true` to use the current cookie-authenticated user.
    pub fn me(&mut self, me: bool) -> &mut Self {
        self.me = me;
        self
    }

    /// Set the list of chapter IDs to mark as read or unread.
    pub fn chapters<I: IntoIterator<Item = ChapterId>>(&mut self, chapters: I) -> &mut Self {
        self.chapters = chapters.into_iter().collect();
        self
    }

    /// Mark the chapters as read or unread.
    pub fn mark_read(&mut self, mark_read: bool) -> &mut Self {
        self.mark_read = mark_read;
        self
    }

    pub async fn send(&self) -> Result<UserChaptersMarkerResponseType, UserRequestError> {
        let user_id = if let Some(id) = self.user_id {
            id.to_string()
        } else if self.me {
            String::from(USER_ME)
        } else {
            return Err(UserRequestError::IDNotProvidedError);
        };

        let body = UserChaptersReadMarkersBody {
            chapters: &self.chapters,
            read: self.mark_read,
        };

        Ok(self
            .md_client
            .client()
            .post(&format!(
                "{base_url}/user/{user_id}/marker",
                base_url = self.md_client.api_url(),
                user_id = user_id
            ))
            .json(&body)
            .send()
            .await?
            .json::<UserChaptersMarkerResponseType>()
            .await?)
    }
}

#[derive(Serialize)]
struct UserChaptersReadMarkersBody<'a> {
    chapters: &'a Vec<ChapterId>,
    read: bool,
}
