#[cfg(feature = "exif")]
use super::exif::ExifDataSource;
use crate::gettext;
use crate::opt::author_name_filter::AuthorFiler;
use crate::opthelper::get_helper;
use crate::pixiv_link::PixivID;
use crate::pixiv_link::ToPixivID;
use crate::pixivapp::illust::PixivAppIllust;
use int_enum::IntEnum;
use json::JsonValue;
use xml::unescape;

/// The type of the artwork
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntEnum)]
pub enum PixivAiType {
    /// Other types
    NonAI = 1,
    /// Artwork is generated by AI.
    AI = 2,
}

impl PixivAiType {
    pub fn is_ai(&self) -> bool {
        *self == Self::AI
    }
}

/// Pixiv's basic data
pub struct PixivData {
    /// ID
    pub id: PixivID,
    /// The title
    pub title: Option<String>,
    /// The author
    pub author: Option<String>,
    pub description: Option<String>,
    /// Tags (Original, translated)
    pub tags: Option<Vec<(String, Option<String>)>>,
    /// AI type
    pub ai_type: Option<PixivAiType>,
}

impl PixivData {
    pub fn new<T: ToPixivID>(id: T) -> Option<Self> {
        let i = id.to_pixiv_id();
        if i.is_none() {
            return None;
        }
        Some(Self {
            id: i.unwrap(),
            title: None,
            author: None,
            description: None,
            tags: None,
            ai_type: None,
        })
    }

    /// Read data from [PixivAppIllust].
    pub fn from_app_illust(&mut self, illust: &PixivAppIllust) {
        self.title = illust.title().map(|s| s.to_owned());
        self.author = illust
            .user_name()
            .map(|s| match get_helper().author_name_filters() {
                Some(l) => l.filter(s),
                None => s.to_owned(),
            });
        self.description = illust.caption().map(|s| s.to_owned());
        let mut tags = Vec::new();
        for i in illust.tags() {
            if let Some(name) = i.name() {
                tags.push((name.to_owned(), i.translated_name().map(|s| s.to_owned())));
            }
        }
        self.tags.replace(tags);
        self.ai_type = match illust.illust_ai_type() {
            Some(t) => match PixivAiType::from_int(t as u8) {
                Ok(t) => Some(t),
                Err(_) => None,
            },
            None => None,
        };
    }

    /// Read data from JSON object.
    /// The object is from `https://www.pixiv.net/artworks/<id>`
    /// * `value` - The JSON object
    /// * `allow_overwrite` - Allow overwrite the data existing.
    pub fn from_web_page_data(&mut self, value: &JsonValue, allow_overwrite: bool) {
        let id: u64 = (&self.id).try_into().unwrap();
        let ids = format!("{}", id);
        self.from_web_page_ajax_data(&value["illust"][ids.as_str()], allow_overwrite)
    }

    /// Read data from JSON object.
    /// The object is from `https://www.pixiv.net/illust/ajax/<id>`
    /// * `value` - The JSON object
    /// * `allow_overwrite` - Allow overwrite the data existing.
    pub fn from_web_page_ajax_data(&mut self, value: &JsonValue, allow_overwrite: bool) {
        if self.title.is_none() || allow_overwrite {
            let title = value["illustTitle"].as_str();
            if title.is_some() {
                self.title = Some(String::from(title.unwrap()));
            }
        }
        if self.author.is_none() || allow_overwrite {
            let author = value["userName"].as_str();
            if author.is_some() {
                let au = author.unwrap();
                match get_helper().author_name_filters() {
                    Some(l) => self.author = Some(l.filter(au)),
                    None => {
                        self.author = Some(String::from(author.unwrap()));
                    }
                }
            }
        }
        if self.description.is_none() || allow_overwrite {
            let mut description = value["description"].as_str();
            if description.is_none() {
                description = value["illustComment"].as_str();
            }
            if description.is_some() {
                let re = unescape(description.unwrap());
                match re {
                    Ok(s) => {
                        self.description = Some(s);
                    }
                    Err(s) => {
                        log::warn!("{} {}", gettext("Failed to unescape string:"), s.as_str());
                    }
                }
            }
        }
        let mut tags = Vec::new();
        for tag in value["tags"]["tags"].members() {
            if let Some(ori) = tag["tag"].as_str() {
                tags.push((
                    ori.to_owned(),
                    match tag["translation"]["en"].as_str() {
                        Some(s) => Some(s.to_owned()),
                        None => None,
                    },
                ));
            }
        }
        self.tags.replace(tags);
        if self.ai_type.is_none() {
            let ai_type = value["aiType"].as_u8();
            match ai_type {
                Some(ai_type) => match PixivAiType::from_int(ai_type) {
                    Ok(ai_type) => {
                        self.ai_type.replace(ai_type);
                    }
                    Err(_) => {}
                },
                None => {}
            }
        }
    }
}

#[cfg(feature = "exif")]
impl ExifDataSource for PixivData {
    fn image_author(&self) -> Option<String> {
        self.author.clone()
    }

    fn image_comment(&self) -> Option<String> {
        self.description.clone()
    }

    fn image_id(&self) -> Option<String> {
        Some(self.id.to_link())
    }

    fn image_title(&self) -> Option<String> {
        self.title.clone()
    }
}
