#[cfg(feature = "chrono")]
use chrono::{DateTime, Utc};
use serde::Deserialize;

use crate::types::{ChapterId, Language, MangaDexResponseStatus, MangaId, StatusCode, UserId};
use crate::v2::responses::partials::{ChapterPartial, GroupPartial};
use crate::v2::responses::{ErrorResponse, ResponseType};

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

impl ResponseType for ChapterResponseType {
    type OkOutput = ChapterResponse;

    /// 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,
        }
    }
}

/// Information about the chapter belonging to a manga.
///
/// This is the response from the `/api/v2/chapter/{id|hash}`.
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq)]
pub struct ChapterResponse {
    code: StatusCode,
    status: MangaDexResponseStatus,
    data: ChapterData,
}

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

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

    pub fn data(&self) -> &ChapterData {
        &self.data
    }
}

/// This struct should not be used directly.
///
/// The page URLs can be assembled with the following format: {server}{hash}/{page_filename}
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ChapterData {
    #[serde(flatten)]
    chapter_partial: ChapterPartial,
    groups: Vec<GroupPartial>,
    status: MangaDexResponseStatus,
    /// Page filenames.
    pages: Vec<String>,
    /// Server where the pages can be accessed.
    server: String,
    /// An alternative server where the pages can be accessed.
    server_fallback: Option<String>,
}

impl ChapterData {
    pub fn id(&self) -> &ChapterId {
        &self.chapter_partial.id()
    }

    pub fn hash(&self) -> &String {
        &self.chapter_partial.hash()
    }

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

    pub fn manga_title(&self) -> &String {
        &self.chapter_partial.manga_title()
    }

    pub fn volume(&self) -> &String {
        &self.chapter_partial.volume()
    }

    pub fn chapter(&self) -> &String {
        &self.chapter_partial.chapter()
    }

    pub fn title(&self) -> &String {
        &self.chapter_partial.title()
    }

    pub fn language(&self) -> &Language {
        &self.chapter_partial.language()
    }

    pub fn uploader(&self) -> &UserId {
        &self.chapter_partial.uploader()
    }

    #[cfg(feature = "chrono")]
    pub fn timestamp(&self) -> &DateTime<Utc> {
        &self.chapter_partial.timestamp()
    }

    #[cfg(not(feature = "chrono"))]
    pub fn timestamp(&self) -> &u64 {
        &self.chapter_partial.timestamp()
    }

    pub fn comments(&self) -> &u64 {
        &self.chapter_partial.comments()
    }

    pub fn views(&self) -> &u64 {
        &self.chapter_partial.views()
    }

    pub fn groups(&self) -> &Vec<GroupPartial> {
        &self.groups
    }

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

    pub fn pages(&self) -> &Vec<String> {
        &self.pages
    }

    pub fn server(&self) -> &String {
        &self.server
    }

    pub fn server_fallback(&self) -> &Option<String> {
        &self.server_fallback
    }
}
