use serde::Deserialize;

use crate::types::{MangaDexResponseStatus, MangaId, StatusCode, UserRating};
use crate::v2::responses::{ErrorResponse, ResponseBody, ResponseType};

#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq)]
#[serde(untagged)]
pub enum UserMangaRatingsResponseType {
    Ok(UserMangaRatingsResponse),
    Err(ErrorResponse),
}

impl ResponseType for UserMangaRatingsResponseType {
    type OkOutput = UserMangaRatingsResponse;

    /// Check if the endpoint response is ok.
    fn is_ok(&self) -> bool {
        matches!(self, Self::Ok(_))
    }

    /// Get the data from the endpoint if the response is ok.
    fn ok(self) -> Option<Self::OkOutput> {
        match self {
            Self::Ok(data) => Some(data),
            _ => None,
        }
    }
}

/// Response from the `/api/v2/user/{id}/ratings` endpoint.
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq)]
pub struct UserMangaRatingsResponse {
    code: StatusCode,
    status: MangaDexResponseStatus,
    data: Vec<UserMangaRatingData>,
}

impl UserMangaRatingsResponse {
    pub fn code(&self) -> &StatusCode {
        &self.code
    }

    pub fn status(&self) -> &MangaDexResponseStatus {
        &self.status
    }

    pub fn data(&self) -> &Vec<UserMangaRatingData> {
        &self.data
    }
}

impl ResponseBody for UserMangaRatingsResponse {}

/// This struct should not be used directly.
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UserMangaRatingData {
    manga_id: MangaId,
    rating: UserRating,
}

impl UserMangaRatingData {
    pub fn manga_id(&self) -> &MangaId {
        &self.manga_id
    }

    pub fn rating(&self) -> &UserRating {
        &self.rating
    }
}
